diff options
Diffstat (limited to 'libPCMutils/src')
-rw-r--r-- | libPCMutils/src/limiter.cpp | 498 | ||||
-rw-r--r-- | libPCMutils/src/pcmutils_lib.cpp | 2254 |
2 files changed, 2235 insertions, 517 deletions
diff --git a/libPCMutils/src/limiter.cpp b/libPCMutils/src/limiter.cpp new file mode 100644 index 0000000..af724f0 --- /dev/null +++ b/libPCMutils/src/limiter.cpp @@ -0,0 +1,498 @@ + +/* ----------------------------------------------------------------------------------------------------------- +Software License for The Fraunhofer FDK AAC Codec Library for Android + +© Copyright 1995 - 2013 Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. + All rights reserved. + + 1. INTRODUCTION +The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software that implements +the MPEG Advanced Audio Coding ("AAC") encoding and decoding scheme for digital audio. +This FDK AAC Codec software is intended to be used on a wide variety of Android devices. + +AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient general perceptual +audio codecs. AAC-ELD is considered the best-performing full-bandwidth communications codec by +independent studies and is widely deployed. AAC has been standardized by ISO and IEC as part +of the MPEG specifications. + +Patent licenses for necessary patent claims for the FDK AAC Codec (including those of Fraunhofer) +may be obtained through Via Licensing (www.vialicensing.com) or through the respective patent owners +individually for the purpose of encoding or decoding bit streams in products that are compliant with +the ISO/IEC MPEG audio standards. Please note that most manufacturers of Android devices already license +these patent claims through Via Licensing or directly from the patent owners, and therefore FDK AAC Codec +software may already be covered under those patent licenses when it is used for those licensed purposes only. + +Commercially-licensed AAC software libraries, including floating-point versions with enhanced sound quality, +are also available from Fraunhofer. Users are encouraged to check the Fraunhofer website for additional +applications information and documentation. + +2. COPYRIGHT LICENSE + +Redistribution and use in source and binary forms, with or without modification, are permitted without +payment of copyright license fees provided that you satisfy the following conditions: + +You must retain the complete text of this software license in redistributions of the FDK AAC Codec or +your modifications thereto in source code form. + +You must retain the complete text of this software license in the documentation and/or other materials +provided with redistributions of the FDK AAC Codec or your modifications thereto in binary form. +You must make available free of charge copies of the complete source code of the FDK AAC Codec and your +modifications thereto to recipients of copies in binary form. + +The name of Fraunhofer may not be used to endorse or promote products derived from this library without +prior written permission. + +You may not charge copyright license fees for anyone to use, copy or distribute the FDK AAC Codec +software or your modifications thereto. + +Your modified versions of the FDK AAC Codec must carry prominent notices stating that you changed the software +and the date of any change. For modified versions of the FDK AAC Codec, the term +"Fraunhofer FDK AAC Codec Library for Android" must be replaced by the term +"Third-Party Modified Version of the Fraunhofer FDK AAC Codec Library for Android." + +3. NO PATENT LICENSE + +NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without limitation the patents of Fraunhofer, +ARE GRANTED BY THIS SOFTWARE LICENSE. Fraunhofer provides no warranty of patent non-infringement with +respect to this software. + +You may use this FDK AAC Codec software or modifications thereto only for purposes that are authorized +by appropriate patent licenses. + +4. DISCLAIMER + +This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright holders and contributors +"AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, including but not limited to the implied warranties +of merchantability and fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary, or consequential damages, +including but not limited to procurement of substitute goods or services; loss of use, data, or profits, +or business interruption, however caused and on any theory of liability, whether in contract, strict +liability, or tort (including negligence), arising in any way out of the use of this software, even if +advised of the possibility of such damage. + +5. CONTACT INFORMATION + +Fraunhofer Institute for Integrated Circuits IIS +Attention: Audio and Multimedia Departments - FDK AAC LL +Am Wolfsmantel 33 +91058 Erlangen, Germany + +www.iis.fraunhofer.de/amm +amm-info@iis.fraunhofer.de +----------------------------------------------------------------------------------------------------------- */ + +/************************ FDK PCM postprocessor module ********************* + + Author(s): Matthias Neusinger + Description: Hard limiter for clipping prevention + +*******************************************************************************/ + +#include "limiter.h" + + +struct TDLimiter { + unsigned int attack; + FIXP_DBL attackConst, releaseConst; + unsigned int attackMs, releaseMs, maxAttackMs; + FIXP_PCM threshold; + unsigned int channels, maxChannels; + unsigned int sampleRate, maxSampleRate; + FIXP_DBL cor, max; + FIXP_DBL* maxBuf; + FIXP_DBL* delayBuf; + unsigned int maxBufIdx, delayBufIdx; + FIXP_DBL smoothState0; + FIXP_DBL minGain; + + FIXP_DBL additionalGainPrev; + FIXP_DBL additionalGainFilterState; + FIXP_DBL additionalGainFilterState1; +}; + +/* create limiter */ +TDLimiterPtr createLimiter( + unsigned int maxAttackMs, + unsigned int releaseMs, + INT_PCM threshold, + unsigned int maxChannels, + unsigned int maxSampleRate + ) +{ + TDLimiterPtr limiter = NULL; + unsigned int attack, release; + FIXP_DBL attackConst, releaseConst, exponent; + INT e_ans; + + /* calc attack and release time in samples */ + attack = (unsigned int)(maxAttackMs * maxSampleRate / 1000); + release = (unsigned int)(releaseMs * maxSampleRate / 1000); + + /* alloc limiter struct */ + limiter = (TDLimiterPtr)FDKcalloc(1, sizeof(struct TDLimiter)); + if (!limiter) return NULL; + + /* alloc max and delay buffers */ + limiter->maxBuf = (FIXP_DBL*)FDKcalloc(attack + 1, sizeof(FIXP_DBL)); + limiter->delayBuf = (FIXP_DBL*)FDKcalloc(attack * maxChannels, sizeof(FIXP_DBL)); + + if (!limiter->maxBuf || !limiter->delayBuf) { + destroyLimiter(limiter); + return NULL; + } + + /* attackConst = pow(0.1, 1.0 / (attack + 1)) */ + exponent = invFixp(attack+1); + attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); + attackConst = scaleValue(attackConst, e_ans); + + /* releaseConst = (float)pow(0.1, 1.0 / (release + 1)) */ + exponent = invFixp(release + 1); + releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); + releaseConst = scaleValue(releaseConst, e_ans); + + /* init parameters */ + limiter->attackMs = maxAttackMs; + limiter->maxAttackMs = maxAttackMs; + limiter->releaseMs = releaseMs; + limiter->attack = attack; + limiter->attackConst = attackConst; + limiter->releaseConst = releaseConst; + limiter->threshold = (FIXP_PCM)threshold; + limiter->channels = maxChannels; + limiter->maxChannels = maxChannels; + limiter->sampleRate = maxSampleRate; + limiter->maxSampleRate = maxSampleRate; + + resetLimiter(limiter); + + return limiter; +} + + +/* reset limiter */ +TDLIMITER_ERROR resetLimiter(TDLimiterPtr limiter) +{ + if (limiter != NULL) { + + limiter->maxBufIdx = 0; + limiter->delayBufIdx = 0; + limiter->max = (FIXP_DBL)0; + limiter->cor = FL2FXCONST_DBL(1.0f/(1<<1)); + limiter->smoothState0 = FL2FXCONST_DBL(1.0f/(1<<1)); + limiter->minGain = FL2FXCONST_DBL(1.0f/(1<<1)); + + limiter->additionalGainPrev = FL2FXCONST_DBL(1.0f/(1<<TDL_GAIN_SCALING)); + limiter->additionalGainFilterState = FL2FXCONST_DBL(1.0f/(1<<TDL_GAIN_SCALING)); + limiter->additionalGainFilterState1 = FL2FXCONST_DBL(1.0f/(1<<TDL_GAIN_SCALING)); + + FDKmemset(limiter->maxBuf, 0, (limiter->attack + 1) * sizeof(FIXP_DBL) ); + FDKmemset(limiter->delayBuf, 0, limiter->attack * limiter->channels * sizeof(FIXP_DBL) ); + } + else { + return TDLIMIT_INVALID_HANDLE; + } + + return TDLIMIT_OK; +} + + +/* destroy limiter */ +TDLIMITER_ERROR destroyLimiter(TDLimiterPtr limiter) +{ + if (limiter != NULL) { + FDKfree(limiter->maxBuf); + FDKfree(limiter->delayBuf); + + FDKfree(limiter); + } + else { + return TDLIMIT_INVALID_HANDLE; + } + return TDLIMIT_OK; +} + +/* apply limiter */ +TDLIMITER_ERROR applyLimiter(TDLimiterPtr limiter, + INT_PCM* samples, + FIXP_DBL* pGain, + const INT* gain_scale, + const UINT gain_size, + const UINT gain_delay, + const UINT nSamples) +{ + unsigned int i, j; + FIXP_PCM tmp1, tmp2; + FIXP_DBL tmp, old, gain, additionalGain, additionalGainUnfiltered; + FIXP_DBL minGain = FL2FXCONST_DBL(1.0f/(1<<1)); + + FDK_ASSERT(gain_size == 1); + FDK_ASSERT(gain_delay <= nSamples); + + if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE; + + { + unsigned int channels = limiter->channels; + unsigned int attack = limiter->attack; + FIXP_DBL attackConst = limiter->attackConst; + FIXP_DBL releaseConst = limiter->releaseConst; + FIXP_DBL threshold = FX_PCM2FX_DBL(limiter->threshold)>>TDL_GAIN_SCALING; + + FIXP_DBL max = limiter->max; + FIXP_DBL* maxBuf = limiter->maxBuf; + unsigned int maxBufIdx = limiter->maxBufIdx; + FIXP_DBL cor = limiter->cor; + FIXP_DBL* delayBuf = limiter->delayBuf; + unsigned int delayBufIdx = limiter->delayBufIdx; + + FIXP_DBL smoothState0 = limiter->smoothState0; + FIXP_DBL additionalGainSmoothState = limiter->additionalGainFilterState; + FIXP_DBL additionalGainSmoothState1 = limiter->additionalGainFilterState1; + + for (i = 0; i < nSamples; i++) { + + if (i < gain_delay) { + additionalGainUnfiltered = limiter->additionalGainPrev; + } else { + additionalGainUnfiltered = pGain[0]; + } + + /* Smooth additionalGain */ + /* [b,a] = butter(1, 0.01) */ + static const FIXP_SGL b[] = { FL2FXCONST_SGL(0.015466*2.0), FL2FXCONST_SGL( 0.015466*2.0) }; + static const FIXP_SGL a[] = { FL2FXCONST_SGL(1.000000), FL2FXCONST_SGL(-0.96907) }; + /* [b,a] = butter(1, 0.001) */ + //static const FIXP_SGL b[] = { FL2FXCONST_SGL(0.0015683*2.0), FL2FXCONST_SGL( 0.0015683*2.0) }; + //static const FIXP_SGL a[] = { FL2FXCONST_SGL(1.0000000), FL2FXCONST_SGL(-0.99686) }; + additionalGain = - fMult(additionalGainSmoothState, a[1]) + fMultDiv2( additionalGainUnfiltered, b[0]) + fMultDiv2(additionalGainSmoothState1, b[1]); + additionalGainSmoothState1 = additionalGainUnfiltered; + additionalGainSmoothState = additionalGain; + + /* Apply the additional scaling that has no delay and no smoothing */ + if (gain_scale[0] > 0) { + additionalGain <<= gain_scale[0]; + } else { + additionalGain >>= gain_scale[0]; + } + + /* get maximum absolute sample value of all channels, including the additional gain. */ + tmp1 = (FIXP_PCM)0; + for (j = 0; j < channels; j++) { + tmp2 = (FIXP_PCM)samples[i * channels + j]; + if (tmp2 == (FIXP_PCM)SAMPLE_MIN) /* protect fAbs from -1.0 value */ + tmp2 = (FIXP_PCM)(SAMPLE_MIN+1); + tmp1 = fMax(tmp1, fAbs(tmp2)); + } + tmp = SATURATE_LEFT_SHIFT(fMultDiv2(tmp1, additionalGain), 1, DFRACT_BITS); + + /* set threshold as lower border to save calculations in running maximum algorithm */ + tmp = fMax(tmp, threshold); + + /* running maximum */ + old = maxBuf[maxBufIdx]; + maxBuf[maxBufIdx] = tmp; + + if (tmp >= max) { + /* new sample is greater than old maximum, so it is the new maximum */ + max = tmp; + } + else if (old < max) { + /* maximum does not change, as the sample, which has left the window was + not the maximum */ + } + else { + /* the old maximum has left the window, we have to search the complete + buffer for the new max */ + max = maxBuf[0]; + for (j = 1; j <= attack; j++) { + if (maxBuf[j] > max) max = maxBuf[j]; + } + } + maxBufIdx++; + if (maxBufIdx >= attack+1) maxBufIdx = 0; + + /* calc gain */ + /* gain is downscaled by one, so that gain = 1.0 can be represented */ + if (max > threshold) { + gain = fDivNorm(threshold, max)>>1; + } + else { + gain = FL2FXCONST_DBL(1.0f/(1<<1)); + } + + /* gain smoothing, method: TDL_EXPONENTIAL */ + /* first order IIR filter with attack correction to avoid overshoots */ + + /* correct the 'aiming' value of the exponential attack to avoid the remaining overshoot */ + if (gain < smoothState0) { + cor = fMin(cor, fMultDiv2((gain - fMultDiv2(FL2FXCONST_SGL(0.1f*(1<<1)),smoothState0)), FL2FXCONST_SGL(1.11111111f/(1<<1)))<<2); + } + else { + cor = gain; + } + + /* smoothing filter */ + if (cor < smoothState0) { + smoothState0 = fMult(attackConst,(smoothState0 - cor)) + cor; /* attack */ + smoothState0 = fMax(smoothState0, gain); /* avoid overshooting target */ + } + else { + /* sign inversion twice to round towards +infinity, + so that gain can converge to 1.0 again, + for bit-identical output when limiter is not active */ + smoothState0 = -fMult(releaseConst,-(smoothState0 - cor)) + cor; /* release */ + } + + gain = smoothState0; + + /* lookahead delay, apply gain */ + for (j = 0; j < channels; j++) { + + tmp = delayBuf[delayBufIdx * channels + j]; + delayBuf[delayBufIdx * channels + j] = fMult((FIXP_PCM)samples[i * channels + j], additionalGain); + + /* Apply gain to delayed signal */ + if (gain < FL2FXCONST_DBL(1.0f/(1<<1))) + tmp = fMult(tmp,gain<<1); + + samples[i * channels + j] = FX_DBL2FX_PCM((FIXP_DBL)SATURATE_LEFT_SHIFT(tmp,TDL_GAIN_SCALING,DFRACT_BITS)); + } + delayBufIdx++; + if (delayBufIdx >= attack) delayBufIdx = 0; + + /* save minimum gain factor */ + if (gain < minGain) minGain = gain; + } + + + limiter->max = max; + limiter->maxBufIdx = maxBufIdx; + limiter->cor = cor; + limiter->delayBufIdx = delayBufIdx; + + limiter->smoothState0 = smoothState0; + limiter->additionalGainFilterState = additionalGainSmoothState; + limiter->additionalGainFilterState1 = additionalGainSmoothState1; + + limiter->minGain = minGain; + + limiter->additionalGainPrev = pGain[0]; + + return TDLIMIT_OK; + } +} + +/* get delay in samples */ +unsigned int getLimiterDelay(TDLimiterPtr limiter) +{ + FDK_ASSERT(limiter != NULL); + return limiter->attack; +} + +/* set number of channels */ +TDLIMITER_ERROR setLimiterNChannels(TDLimiterPtr limiter, unsigned int nChannels) +{ + if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE; + + if (nChannels > limiter->maxChannels) return TDLIMIT_INVALID_PARAMETER; + + limiter->channels = nChannels; + //resetLimiter(limiter); + + return TDLIMIT_OK; +} + +/* set sampling rate */ +TDLIMITER_ERROR setLimiterSampleRate(TDLimiterPtr limiter, unsigned int sampleRate) +{ + unsigned int attack, release; + FIXP_DBL attackConst, releaseConst, exponent; + INT e_ans; + + if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE; + + if (sampleRate > limiter->maxSampleRate) return TDLIMIT_INVALID_PARAMETER; + + /* update attack and release time in samples */ + attack = (unsigned int)(limiter->attackMs * sampleRate / 1000); + release = (unsigned int)(limiter->releaseMs * sampleRate / 1000); + + /* attackConst = pow(0.1, 1.0 / (attack + 1)) */ + exponent = invFixp(attack+1); + attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); + attackConst = scaleValue(attackConst, e_ans); + + /* releaseConst = (float)pow(0.1, 1.0 / (release + 1)) */ + exponent = invFixp(release + 1); + releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); + releaseConst = scaleValue(releaseConst, e_ans); + + limiter->attack = attack; + limiter->attackConst = attackConst; + limiter->releaseConst = releaseConst; + limiter->sampleRate = sampleRate; + + /* reset */ + //resetLimiter(limiter); + + return TDLIMIT_OK; +} + +/* set attack time */ +TDLIMITER_ERROR setLimiterAttack(TDLimiterPtr limiter, unsigned int attackMs) +{ + unsigned int attack; + FIXP_DBL attackConst, exponent; + INT e_ans; + + if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE; + + if (attackMs > limiter->maxAttackMs) return TDLIMIT_INVALID_PARAMETER; + + /* calculate attack time in samples */ + attack = (unsigned int)(attackMs * limiter->sampleRate / 1000); + + /* attackConst = pow(0.1, 1.0 / (attack + 1)) */ + exponent = invFixp(attack+1); + attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); + attackConst = scaleValue(attackConst, e_ans); + + limiter->attack = attack; + limiter->attackConst = attackConst; + limiter->attackMs = attackMs; + + return TDLIMIT_OK; +} + +/* set release time */ +TDLIMITER_ERROR setLimiterRelease(TDLimiterPtr limiter, unsigned int releaseMs) +{ + unsigned int release; + FIXP_DBL releaseConst, exponent; + INT e_ans; + + if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE; + + /* calculate release time in samples */ + release = (unsigned int)(releaseMs * limiter->sampleRate / 1000); + + /* releaseConst = (float)pow(0.1, 1.0 / (release + 1)) */ + exponent = invFixp(release + 1); + releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); + releaseConst = scaleValue(releaseConst, e_ans); + + limiter->releaseConst = releaseConst; + limiter->releaseMs = releaseMs; + + return TDLIMIT_OK; +} + +/* set limiter threshold */ +TDLIMITER_ERROR setLimiterThreshold(TDLimiterPtr limiter, INT_PCM threshold) +{ + if ( limiter == NULL ) return TDLIMIT_INVALID_HANDLE; + + limiter->threshold = (FIXP_PCM)threshold; + + return TDLIMIT_OK; +} diff --git a/libPCMutils/src/pcmutils_lib.cpp b/libPCMutils/src/pcmutils_lib.cpp index a84a050..32d8437 100644 --- a/libPCMutils/src/pcmutils_lib.cpp +++ b/libPCMutils/src/pcmutils_lib.cpp @@ -84,8 +84,8 @@ amm-info@iis.fraunhofer.de /**************************** FDK PCM utils module ************************** Author(s): Christian Griebel - Description: Defines functions to interface with the PCM post processing - module. + Description: Defines functions that perform downmixing or a simple channel + expansion in the PCM time domain. *******************************************************************************/ @@ -93,21 +93,66 @@ amm-info@iis.fraunhofer.de #include "genericStds.h" #include "fixpoint_math.h" +#include "FDK_core.h" + + +/* ------------------------ * + * GLOBAL SETTINGS (GFR): * + * ------------------------ */ +#define DSE_METADATA_ENABLE /*!< Enable this to support MPEG/ETSI DVB ancillary data for + encoder assisted downmixing of MPEG-4 AAC and + MPEG-1/2 layer 2 streams. */ +#define PCE_METADATA_ENABLE /*!< Enable this to support MPEG matrix mixdown with a + coefficient carried in the PCE. */ + +#define PCM_DMX_MAX_IN_CHANNELS ( 8 ) /* Neither the maximum number of input nor the maximum number of output channels ... */ +#define PCM_DMX_MAX_OUT_CHANNELS ( 8 ) /* ... must exceed the maximum number of channels that the framework can handle. */ + +/* ------------------------ * + * SPECIFIC SETTINGS: * + * ------------------------ */ +#define PCM_CHANNEL_EXTENSION_ENABLE /*!< Allow module to duplicate mono signals or add zero channels to achieve the + desired number of output channels. */ + +#define PCM_DMX_DFLT_MAX_OUT_CHANNELS ( 6 ) /*!< The maximum number of output channels. If the value is greater than 0 the module + will automatically create a mixdown for all input signals with more channels + than specified. */ +#define PCM_DMX_DFLT_MIN_OUT_CHANNELS ( 0 ) /*!< The minimum number of output channels. If the value is greater than 0 the module + will do channel extension automatically for all input signals with less channels + than specified. */ +#define PCM_DMX_MAX_DELAY_FRAMES ( 1 ) /*!< The maximum delay frames to align the bitstreams payload with the PCM output. */ +#define PCM_DMX_DFLT_EXPIRY_FRAME ( 50 ) /*!< If value is greater than 0 the mixdown coefficients will expire by default after the + given number of frames. The value 50 corresponds to at least 500ms (FL 960 @ 96kHz) */ +/* #define PCMDMX_DEBUG */ + +/* Derived setting: + * No need to edit beyond this line. */ +#if defined(DSE_METADATA_ENABLE) || defined(PCE_METADATA_ENABLE) || defined(ARIB_MIXDOWN_ENABLE) + #define PCM_DOWNMIX_ENABLE /*!< Generally enable down mixing. */ +#endif +#if (PCM_DMX_MAX_IN_CHANNELS > 2) || (PCM_DMX_MAX_OUT_CHANNELS > 2) + #define PCM_DMX_MAX_CHANNELS ( 8 ) + #define PCM_DMX_MAX_CHANNEL_GROUPS ( 4 ) + #define PCM_DMX_MAX_CHANNELS_PER_GROUP PCM_DMX_MAX_CHANNELS /* All channels can be in one group */ +#else + #define PCM_DMX_MAX_CHANNELS ( 3 ) /* Need to add 1 because there are three channel positions in first channel group. */ + #define PCM_DMX_MAX_CHANNEL_GROUPS ( 1 ) /* Only front channels supported. */ + #define PCM_DMX_MAX_CHANNELS_PER_GROUP ( 2 ) /* The maximum over all channel groups */ +#endif +#if (PCM_DMX_MAX_IN_CHANNELS > PCM_DMX_MAX_OUT_CHANNELS) + #define PCM_DMX_MAX_IO_CHANNELS PCM_DMX_MAX_IN_CHANNELS +#else + #define PCM_DMX_MAX_IO_CHANNELS PCM_DMX_MAX_OUT_CHANNELS +#endif /* Decoder library info */ #define PCMDMX_LIB_VL0 2 #define PCMDMX_LIB_VL1 4 -#define PCMDMX_LIB_VL2 0 +#define PCMDMX_LIB_VL2 2 #define PCMDMX_LIB_TITLE "PCM Downmix Lib" #define PCMDMX_LIB_BUILD_DATE __DATE__ #define PCMDMX_LIB_BUILD_TIME __TIME__ -/* Library settings */ -#define PCM_DMX_MAX_DELAY_FRAMES ( 1 ) -#define PCM_DMX_MAX_CHANNELS ( 8 ) -#define PCM_DMX_MAX_CHANNEL_GROUPS ( 4 ) -#define PCM_DMX_MAX_CHANNELS_PER_GROUP ( 3 ) /* The maximum over all groups */ -#define PCMDMX_DFLT_EXPIRY_FRAME ( 50 ) /* At least 500ms (FL 960 @ 96kHz) */ /* Fixed and unique channel group indices. * The last group index has to be smaller than PCM_DMX_MAX_CHANNEL_GROUPS. */ @@ -122,22 +167,64 @@ amm-info@iis.fraunhofer.de #define CENTER_FRONT_CHANNEL ( 0 ) /* C */ #define LEFT_FRONT_CHANNEL ( 1 ) /* L */ #define RIGHT_FRONT_CHANNEL ( 2 ) /* R */ -#define LEFT_OUTSIDE_CHANNEL ( 3 ) /* Lo */ -#define RIGHT_OUTSIDE_CHANNEL ( 4 ) /* Ro */ -#define LEFT_REAR_CHANNEL ( 5 ) /* Lr aka left back channel */ -#define RIGHT_REAR_CHANNEL ( 6 ) /* Rr aka right back channel */ -#define LOW_FREQUENCY_CHANNEL ( 7 ) /* Lf */ +#define LEFT_REAR_CHANNEL ( 3 ) /* Lr (aka left back channel) or center back channel */ +#define RIGHT_REAR_CHANNEL ( 4 ) /* Rr (aka right back channel) */ +#define LOW_FREQUENCY_CHANNEL ( 5 ) /* Lf */ +#define LEFT_MULTIPRPS_CHANNEL ( 6 ) /* Left multipurpose channel */ +#define RIGHT_MULTIPRPS_CHANNEL ( 7 ) /* Right multipurpose channel */ /* More constants */ -#define ANC_DATA_SYNC_BYTE ( 0xBC ) /* ancillary data sync byte. */ -#define ATTENUATION_FACTOR_1 ( FL2FXCONST_SGL(0.70710678f) ) +#define ONE_CHANNEL ( 1 ) #define TWO_CHANNEL ( 2 ) +#define SIX_CHANNEL ( 6 ) +#define EIGHT_CHANNEL ( 8 ) + +#define PCMDMX_A_IDX_DEFAULT ( 2 ) +#define PCMDMX_B_IDX_DEFAULT ( 2 ) +#define PCMDMX_LFE_IDX_DEFAULT ( 15 ) +#define PCMDMX_GAIN_5_DEFAULT ( 0 ) +#define PCMDMX_GAIN_2_DEFAULT ( 0 ) + +#define PCMDMX_MAX_HEADROOM ( 3 ) /* Defines the maximum PCM scaling headroom that can be done by a + postprocessing step. This value must be greater or equal to 0. */ + +#define FALSE 0 +#define TRUE 1 +#define IN 0 +#define OUT 1 + +/* Type definitions: */ +#ifndef DMX_HIGH_PRECISION_ENABLE + #define FIXP_DMX FIXP_SGL + #define FX_DMX2FX_DBL(x) FX_SGL2FX_DBL((FIXP_SGL)(x)) + #define FX_DBL2FX_DMX(x) FX_DBL2FX_SGL(x) + #define FL2FXCONST_DMX(x) FL2FXCONST_SGL(x) + #define MAXVAL_DMX MAXVAL_SGL + #define FX_DMX2SHRT(x) ((SHORT)(x)) + #define FX_DMX2FL(x) FX_DBL2FL(FX_DMX2FX_DBL(x)) +#else + #define FIXP_DMX FIXP_DBL + #define FX_DMX2FX_DBL(x) ((FIXP_DBL)(x)) + #define FX_DBL2FX_DMX(x) ((FIXP_DBL)(x) + #define FL2FXCONST_DMX(x) FL2FXCONST_DBL(x) + #define MAXVAL_DMX MAXVAL_DBL + #define FX_DMX2SHRT(x) ((SHORT)((x)>>FRACT_BITS)) + #define FX_DMX2FL(x) FX_DBL2FL(x) +#endif -/* Sanity checks on library setting: */ +/* The number of channels positions for each group in the internal representation. + * See the channel labels above. */ +static const UCHAR maxChInGrp[PCM_DMX_MAX_CHANNEL_GROUPS] = { +#if (PCM_DMX_MAX_CHANNELS > 3) + 3, 0, 2, 1 +#else + PCM_DMX_MAX_CHANNELS_PER_GROUP +#endif +}; /* List of packed channel modes */ typedef enum -{ /* CH_MODE_<numFrontCh>_<numOutsideCh>_<numRearCh>_<numLfCh> */ +{ /* CH_MODE_<numFrontCh>_<numSideCh>_<numBackCh>_<numLfCh> */ CH_MODE_UNDEFINED = 0x0000, /* 1 channel */ CH_MODE_1_0_0_0 = 0x0001, /* chCfg 1 */ @@ -151,53 +238,93 @@ typedef enum CH_MODE_3_0_1_0 = 0x0103, /* chCfg 4 */ CH_MODE_2_0_2_0 = 0x0202, CH_MODE_2_0_1_1 = 0x1102, + CH_MODE_4_0_0_0 = 0x0004, /* 5 channels */ CH_MODE_3_0_2_0 = 0x0203, /* chCfg 5 */ CH_MODE_2_0_2_1 = 0x1202, CH_MODE_3_0_1_1 = 0x1103, CH_MODE_3_2_0_0 = 0x0023, + CH_MODE_5_0_0_0 = 0x0005, /* 6 channels */ CH_MODE_3_0_2_1 = 0x1203, /* chCfg 6 */ + CH_MODE_3_2_0_1 = 0x1023, CH_MODE_3_2_1_0 = 0x0123, + CH_MODE_5_0_1_0 = 0x0105, + CH_MODE_6_0_0_0 = 0x0006, /* 7 channels */ CH_MODE_2_2_2_1 = 0x1222, + CH_MODE_3_0_3_1 = 0x1303, /* chCfg 11 */ CH_MODE_3_2_1_1 = 0x1123, CH_MODE_3_2_2_0 = 0x0223, + CH_MODE_3_0_2_2 = 0x2203, + CH_MODE_5_0_2_0 = 0x0205, + CH_MODE_5_0_1_1 = 0x1105, + CH_MODE_7_0_0_0 = 0x0007, /* 8 channels */ - CH_MODE_3_2_2_1 = 0x1222, /* chCfg 7 */ + CH_MODE_3_2_2_1 = 0x1223, + CH_MODE_3_0_4_1 = 0x1403, /* chCfg 12 */ + CH_MODE_5_0_2_1 = 0x1205, /* chCfg 7 + 14 */ + CH_MODE_5_2_1_0 = 0x0125, CH_MODE_3_2_1_2 = 0x2123, - CH_MODE_2_2_2_2 = 0x2222 + CH_MODE_2_2_2_2 = 0x2222, + CH_MODE_3_0_3_2 = 0x2303, + CH_MODE_8_0_0_0 = 0x0008 } PCM_DMX_CHANNEL_MODE; /* These are the channel configurations linked to the number of output channels give by the user: */ -static const PCM_DMX_CHANNEL_MODE outChModeTable[PCM_DMX_MAX_CHANNELS] = +static const PCM_DMX_CHANNEL_MODE outChModeTable[PCM_DMX_MAX_CHANNELS+1] = { + CH_MODE_UNDEFINED, CH_MODE_1_0_0_0, /* 1 channel */ CH_MODE_2_0_0_0, /* 2 channels */ - CH_MODE_3_0_0_0, /* 3 channels */ - CH_MODE_3_0_1_0, /* 4 channels */ + CH_MODE_3_0_0_0 /* 3 channels */ +#if (PCM_DMX_MAX_CHANNELS > 3) + ,CH_MODE_3_0_1_0, /* 4 channels */ CH_MODE_3_0_2_0, /* 5 channels */ CH_MODE_3_0_2_1, /* 6 channels */ - CH_MODE_3_2_2_0, /* 7 channels */ - CH_MODE_3_2_2_1 /* 8 channels */ + CH_MODE_3_0_3_1, /* 7 channels */ + CH_MODE_3_0_4_1 /* 8 channels */ +#endif }; -static const FIXP_SGL dvbDownmixFactors[8] = +static const FIXP_DMX abMixLvlValueTab[8] = { - FL2FXCONST_SGL(1.0f), - FL2FXCONST_SGL(0.841f), - FL2FXCONST_SGL(0.707f), - FL2FXCONST_SGL(0.596f), - FL2FXCONST_SGL(0.500f), - FL2FXCONST_SGL(0.422f), - FL2FXCONST_SGL(0.355f), - FL2FXCONST_SGL(0.0f) + FL2FXCONST_DMX(0.500f), /* scaled by 1 */ + FL2FXCONST_DMX(0.841f), + FL2FXCONST_DMX(0.707f), + FL2FXCONST_DMX(0.596f), + FL2FXCONST_DMX(0.500f), + FL2FXCONST_DMX(0.422f), + FL2FXCONST_DMX(0.355f), + FL2FXCONST_DMX(0.0f) +}; + +static const FIXP_DMX lfeMixLvlValueTab[16] = +{ /* value, scale */ + FL2FXCONST_DMX(0.7905f), /* 2 */ + FL2FXCONST_DMX(0.5000f), /* 2 */ + FL2FXCONST_DMX(0.8395f), /* 1 */ + FL2FXCONST_DMX(0.7065f), /* 1 */ + FL2FXCONST_DMX(0.5945f), /* 1 */ + FL2FXCONST_DMX(0.500f), /* 1 */ + FL2FXCONST_DMX(0.841f), /* 0 */ + FL2FXCONST_DMX(0.707f), /* 0 */ + FL2FXCONST_DMX(0.596f), /* 0 */ + FL2FXCONST_DMX(0.500f), /* 0 */ + FL2FXCONST_DMX(0.316f), /* 0 */ + FL2FXCONST_DMX(0.178f), /* 0 */ + FL2FXCONST_DMX(0.100f), /* 0 */ + FL2FXCONST_DMX(0.032f), /* 0 */ + FL2FXCONST_DMX(0.010f), /* 0 */ + FL2FXCONST_DMX(0.000f) /* 0 */ }; + +#ifdef PCE_METADATA_ENABLE /* MPEG matrix mixdown: Set 1: L' = (1 + 2^-0.5 + A )^-1 * [L + C * 2^-0.5 + A * Ls]; R' = (1 + 2^-0.5 + A )^-1 * [R + C * 2^-0.5 + A * Rs]; @@ -207,111 +334,176 @@ static const FIXP_SGL dvbDownmixFactors[8] = M = (3 + 2A)^-1 * [L + C + R + A*(Ls + Rs)]; */ - static const FIXP_SGL mpegMixDownIdx2Coef[4] = + static const FIXP_DMX mpegMixDownIdx2Coef[4] = { - FL2FXCONST_SGL(0.70710678f), - FL2FXCONST_SGL(0.5f), - FL2FXCONST_SGL(0.35355339f), - FL2FXCONST_SGL(0.0f) + FL2FXCONST_DMX(0.70710678f), + FL2FXCONST_DMX(0.5f), + FL2FXCONST_DMX(0.35355339f), + FL2FXCONST_DMX(0.0f) }; - static const FIXP_SGL mpegMixDownIdx2PreFact[4] = - { - FL2FXCONST_SGL(0.4142135623730950f), - FL2FXCONST_SGL(0.4530818393219728f), - FL2FXCONST_SGL(0.4852813742385703f), - FL2FXCONST_SGL(0.5857864376269050f) + static const FIXP_SGL mpegMixDownIdx2PreFact[3][4] = + { { /* Set 1: */ + FL2FXCONST_DMX(0.4142135623730950f), + FL2FXCONST_DMX(0.4530818393219728f), + FL2FXCONST_DMX(0.4852813742385703f), + FL2FXCONST_DMX(0.5857864376269050f) + },{ /* Set 2: */ + FL2FXCONST_DMX(0.3203772410170407f), + FL2FXCONST_DMX(0.3693980625181293f), + FL2FXCONST_DMX(0.4142135623730950f), + FL2FXCONST_DMX(0.5857864376269050f) + },{ /* Mono DMX set: */ + FL2FXCONST_DMX(0.2265409196609864f), + FL2FXCONST_DMX(0.25f), + FL2FXCONST_DMX(0.2697521433898179f), + FL2FXCONST_DMX(0.3333333333333333f) } }; +#endif /* PCE_METADATA_ENABLE */ - typedef struct - { - USHORT matrixMixdownIdx; /*!< MPEG mixdown index extracted from PCE. */ - USHORT pseudoSurroundEnable; /*!< Pseudo surround enable flag extracted from PCE. */ - USHORT mixdownAvailable; /*!< Will be set to 1 if we found a valid coefficient. */ - - } MPEG_MIXDOWN_INFO; +#define TYPE_NONE ( 0x0 ) +#define TYPE_DSE_DATA ( 0x1 ) +#define TYPE_PCE_DATA ( 0x2 ) typedef struct { - FIXP_SGL centerMixLevelValue; /*!< DVB mixdown level for the center channel extracted from anc data. */ - FIXP_SGL surroundMixLevelValue; /*!< DVB mixdown level for back channels extracted from anc data. */ - - UCHAR mixLevelsAvail; /*!< Will be set to 1 if we found a valid coefficient. */ + UINT typeFlags; + /* From DSE */ + UCHAR cLevIdx; + UCHAR sLevIdx; + UCHAR dmixIdxA; + UCHAR dmixIdxB; + UCHAR dmixIdxLfe; + UCHAR dmxGainIdx2; + UCHAR dmxGainIdx5; +#ifdef PCE_METADATA_ENABLE + /* From PCE */ + UCHAR matrixMixdownIdx; +#endif + /* Attributes: */ + SCHAR pseudoSurround; /*!< If set to 1 the signal is pseudo surround compatible. The value 0 tells + that it is not. If the value is -1 the information is not available. */ + UINT expiryCount; /*!< Counter to monitor the life time of a meta data set. */ + +} DMX_BS_META_DATA; + +/* Default metadata */ +static const DMX_BS_META_DATA dfltMetaData = { + 0, 2, 2, 2, 2, 15, 0, 0, +#ifdef PCE_METADATA_ENABLE + 0, +#endif + -1, 0 +}; -} DVB_MIXDOWN_LEVELS; +/* Dynamic (user) params: + See the definition of PCMDMX_PARAM for details on the specific fields. */ +typedef struct +{ + UINT expiryFrame; /*!< Linked to DMX_BS_DATA_EXPIRY_FRAME */ + DUAL_CHANNEL_MODE dualChannelMode; /*!< Linked to DMX_DUAL_CHANNEL_MODE */ + PSEUDO_SURROUND_MODE pseudoSurrMode; /*!< Linked to DMX_PSEUDO_SURROUND_MODE */ + SHORT numOutChannelsMin; /*!< Linked to MIN_NUMBER_OF_OUTPUT_CHANNELS */ + SHORT numOutChannelsMax; /*!< Linked to MAX_NUMBER_OF_OUTPUT_CHANNELS */ + UCHAR frameDelay; /*!< Linked to DMX_BS_DATA_DELAY */ +} PCM_DMX_USER_PARAMS; /* Modules main data structure: */ struct PCM_DMX_INSTANCE { - DVB_MIXDOWN_LEVELS dvbMixDownLevels[PCM_DMX_MAX_DELAY_FRAMES+1]; - MPEG_MIXDOWN_INFO mpegMixDownInfo[PCM_DMX_MAX_DELAY_FRAMES+1]; - DUAL_CHANNEL_MODE dualChannelMode; - UINT expiryFrame; - UINT expiryCount; - SHORT numOutputChannels; - UCHAR applyProcessing; - UCHAR frameDelay; + /* Metadata */ + DMX_BS_META_DATA bsMetaData[PCM_DMX_MAX_DELAY_FRAMES+1]; + PCM_DMX_USER_PARAMS userParams; + + UCHAR applyProcessing; /*!< Flag to en-/disable modules processing. + The max channel limiting is done independently. */ }; /* Memory allocation macro */ C_ALLOC_MEM_STATIC(PcmDmxInstance, struct PCM_DMX_INSTANCE, 1) -/** Evaluate a given channel configuration and extract a packed channel mode and generate a channel offset table +/** Evaluate a given channel configuration and extract a packed channel mode. In addition the + * function generates a channel offset table for the mapping to the internal representation. * This function is the inverse to the getChannelDescription() routine. * @param [in] The total number of channels of the given configuration. * @param [in] Array holding the corresponding channel types for each channel. * @param [in] Array holding the corresponding channel type indices for each channel. * @param [out] Array where the buffer offsets for each channel are stored into. - * @returns Returns the packed channel mode. + * @param [out] The generated packed channel mode that represents the given input configuration. + * @returns Returns an error code. **/ static -PCM_DMX_CHANNEL_MODE getChannelMode ( +PCMDMX_ERROR getChannelMode ( const INT numChannels, /* in */ const AUDIO_CHANNEL_TYPE channelType[], /* in */ const UCHAR channelIndices[], /* in */ - UCHAR offsetTable[PCM_DMX_MAX_CHANNELS] /* out */ + UCHAR offsetTable[PCM_DMX_MAX_CHANNELS], /* out */ + PCM_DMX_CHANNEL_MODE *chMode /* out */ ) { - UINT chMode = CH_MODE_UNDEFINED; UCHAR chIdx[PCM_DMX_MAX_CHANNEL_GROUPS][PCM_DMX_MAX_CHANNELS_PER_GROUP]; - UCHAR numChInGrp[PCM_DMX_MAX_CHANNEL_GROUPS]; - int ch, grpIdx, err = 0; + UCHAR numChInGrp[PCM_DMX_MAX_CHANNEL_GROUPS]; /* Total num of channels per group of the input config */ + UCHAR numChFree[PCM_DMX_MAX_CHANNEL_GROUPS]; /* Number of free slots per group in the internal repr. */ + UCHAR hardToPlace[PCM_DMX_MAX_CHANNELS]; /* List of channels not matching the internal repr. */ + UCHAR h2pSortIdx[PCM_DMX_MAX_CHANNELS]; + PCMDMX_ERROR err = PCMDMX_OK; + int ch, grpIdx; + int numChToPlace = 0; FDK_ASSERT(channelType != NULL); FDK_ASSERT(channelIndices != NULL); FDK_ASSERT(offsetTable != NULL); + FDK_ASSERT(chMode != NULL); /* For details see ISO/IEC 13818-7:2005(E), 8.5.3 Channel configuration */ FDKmemclear(numChInGrp, PCM_DMX_MAX_CHANNEL_GROUPS*sizeof(UCHAR)); FDKmemset(offsetTable, 255, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR)); FDKmemset(chIdx, 255, PCM_DMX_MAX_CHANNEL_GROUPS*PCM_DMX_MAX_CHANNELS_PER_GROUP*sizeof(UCHAR)); + FDKmemset(hardToPlace, 255, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR)); + FDKmemset(h2pSortIdx, 255, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR)); + /* Get the restrictions of the internal representation */ + FDKmemcpy(numChFree, maxChInGrp, PCM_DMX_MAX_CHANNEL_GROUPS*sizeof(UCHAR)); + + *chMode = CH_MODE_UNDEFINED; /* Categorize channels */ for (ch = 0; ch < numChannels; ch += 1) { - int i = 0, j, chGrpIdx = channelIndices[ch]; + UCHAR chGrpIdx = channelIndices[ch]; + int i = 0, j; switch (channelType[ch]) { - case ACT_FRONT: case ACT_FRONT_TOP: + chGrpIdx += numChInGrp[CH_GROUP_FRONT]; /* Append after normal plain */ + case ACT_FRONT: grpIdx = CH_GROUP_FRONT; break; - case ACT_SIDE: +#if (PCM_DMX_MAX_CHANNEL_GROUPS > 1) case ACT_SIDE_TOP: + chGrpIdx += numChInGrp[CH_GROUP_SIDE]; /* Append after normal plain */ + case ACT_SIDE: grpIdx = CH_GROUP_SIDE; break; - case ACT_BACK: case ACT_BACK_TOP: + chGrpIdx += numChInGrp[CH_GROUP_REAR]; /* Append after normal plain */ + case ACT_BACK: grpIdx = CH_GROUP_REAR; break; case ACT_LFE: grpIdx = CH_GROUP_LFE; break; +#endif default: - err = -1; - continue; + /* Found a channel that can not be categorized! Most likely due to corrupt input signalling. + The rescue strategy is to append it to the front channels (=> ignore index). + This could cause strange behaviour so return an error to signal it. */ + err = PCMDMX_INVALID_MODE; + grpIdx = CH_GROUP_FRONT; + chGrpIdx = numChannels + numChToPlace; + numChToPlace += 1; + break; } if (numChInGrp[grpIdx] < PCM_DMX_MAX_CHANNELS_PER_GROUP) { @@ -327,76 +519,152 @@ PCM_DMX_CHANNEL_MODE getChannelMode ( } } - /* Compose channel offset table */ +#if (PCM_DMX_MAX_CHANNEL_GROUPS > 1) + FDK_ASSERT( (numChInGrp[CH_GROUP_FRONT]+numChInGrp[CH_GROUP_SIDE] + +numChInGrp[CH_GROUP_REAR]+numChInGrp[CH_GROUP_LFE]) == numChannels); +#else + FDK_ASSERT( numChInGrp[CH_GROUP_FRONT] == numChannels ); +#endif + + /* Compose channel offset table: + * Map all channels to the internal representation. */ + numChToPlace = 0; /* Non-symmetric channels */ if (numChInGrp[CH_GROUP_FRONT] & 0x1) { /* Odd number of front channels -> we have a center channel. In MPEG-4 the center has the index 0. */ offsetTable[CENTER_FRONT_CHANNEL] = chIdx[CH_GROUP_FRONT][0]; + numChFree[CH_GROUP_FRONT] -= 1; } for (grpIdx = 0; grpIdx < PCM_DMX_MAX_CHANNEL_GROUPS; grpIdx += 1) { - int chMapPos, maxChannels = 0; - ch = 0; + int chMapPos = 0; + ch = 0; /* Index of channel within the specific group */ switch (grpIdx) { case CH_GROUP_FRONT: chMapPos = LEFT_FRONT_CHANNEL; - maxChannels = 3; ch = numChInGrp[grpIdx] & 0x1; break; +#if (PCM_DMX_MAX_CHANNEL_GROUPS > 1) case CH_GROUP_SIDE: - chMapPos = LEFT_OUTSIDE_CHANNEL; - maxChannels = 2; break; case CH_GROUP_REAR: chMapPos = LEFT_REAR_CHANNEL; - maxChannels = 2; break; case CH_GROUP_LFE: chMapPos = LOW_FREQUENCY_CHANNEL; - maxChannels = 1; break; +#endif default: - err = -1; + FDK_ASSERT(0); continue; } + /* Map all channels of the group */ for ( ; ch < numChInGrp[grpIdx]; ch += 1) { - if (ch < maxChannels) { + if (numChFree[grpIdx] > 0) { offsetTable[chMapPos] = chIdx[grpIdx][ch]; chMapPos += 1; + numChFree[grpIdx] -= 1; } else { - err = -1; + /* Add to the list of hardship cases considering a MPEG-like sorting order: */ + int pos, sortIdx = grpIdx*PCM_DMX_MAX_CHANNELS_PER_GROUP + channelIndices[chIdx[grpIdx][ch]]; + for (pos = numChToPlace; pos > 0; pos -= 1) { + if (h2pSortIdx[pos-1] > sortIdx) { + hardToPlace[pos] = hardToPlace[pos-1]; + h2pSortIdx[pos] = h2pSortIdx[pos-1]; + } else { + /* Insert channel at the current index/position */ + break; + } + } + hardToPlace[pos] = chIdx[grpIdx][ch]; + h2pSortIdx[pos] = sortIdx; + numChToPlace += 1; } } } - if (err == 0) { - /* Compose the channel mode */ - chMode = (numChInGrp[CH_GROUP_LFE] & 0xF) << 12 - | (numChInGrp[CH_GROUP_REAR] & 0xF) << 8 - | (numChInGrp[CH_GROUP_SIDE] & 0xF) << 4 - | (numChInGrp[CH_GROUP_FRONT] & 0xF); + { /* Assign the hardship cases */ + int chMapPos = 0; + int mappingHeat = 0; + for (ch = 0; ch < numChToPlace; ch+=1) { + int chAssigned = 0; + + /* Just assigning the channels to the next best slot can lead to undesired results (especially for x/x/1.x + configurations). Thus use the MPEG-like sorting index to find the best fitting slot for each channel. + If this is not possible the sorting index will be ignored (mappingHeat >= 2). */ + for ( ; chMapPos < PCM_DMX_MAX_CHANNELS; chMapPos+=1) { + if (offsetTable[chMapPos] == 255) { + int prvSortIdx = 0; + int nxtSortIdx = (CH_GROUP_LFE+1)*PCM_DMX_MAX_CHANNELS_PER_GROUP; + + if (mappingHeat < 2) { + if (chMapPos < LEFT_REAR_CHANNEL) { + /* Got front channel slot */ + prvSortIdx = CH_GROUP_FRONT*PCM_DMX_MAX_CHANNELS_PER_GROUP + chMapPos - CENTER_FRONT_CHANNEL; + nxtSortIdx = CH_GROUP_SIDE *PCM_DMX_MAX_CHANNELS_PER_GROUP; + } + else if (chMapPos < LOW_FREQUENCY_CHANNEL) { + /* Got back channel slot */ + prvSortIdx = CH_GROUP_REAR*PCM_DMX_MAX_CHANNELS_PER_GROUP + chMapPos - LEFT_REAR_CHANNEL; + nxtSortIdx = CH_GROUP_LFE *PCM_DMX_MAX_CHANNELS_PER_GROUP; + } + else if (chMapPos < LEFT_MULTIPRPS_CHANNEL) { + /* Got lfe channel slot */ + prvSortIdx = CH_GROUP_LFE *PCM_DMX_MAX_CHANNELS_PER_GROUP + chMapPos - LOW_FREQUENCY_CHANNEL; + nxtSortIdx = (CH_GROUP_LFE+1)*PCM_DMX_MAX_CHANNELS_PER_GROUP; + } + } + + /* Assign the channel only if its sort index is within the range */ + if ( (h2pSortIdx[ch] >= prvSortIdx) + && (h2pSortIdx[ch] < nxtSortIdx) ) { + offsetTable[chMapPos++] = hardToPlace[ch]; + chAssigned = 1; + break; + } + } + } + if (chAssigned == 0) { + chMapPos = 0; + ch -= 1; + mappingHeat += 1; + continue; + } + } } - return (PCM_DMX_CHANNEL_MODE)chMode; + /* Compose the channel mode */ + *chMode = (PCM_DMX_CHANNEL_MODE)( (numChInGrp[CH_GROUP_FRONT] & 0xF) +#if (PCM_DMX_MAX_CHANNEL_GROUPS > 1) + | (numChInGrp[CH_GROUP_SIDE] & 0xF) << 4 + | (numChInGrp[CH_GROUP_REAR] & 0xF) << 8 + | (numChInGrp[CH_GROUP_LFE] & 0xF) << 12 +#endif + ); + + return err; } /** Generate a channel offset table and complete channel description for a given (packed) channel mode. - * This function is the inverse to the getChannelMode() routine. - * @param [in] The total number of channels of the given configuration. + * This function is the inverse to the getChannelMode() routine but does not support weird channel + * configurations. All channels have to be in the normal height layer and there must not be more + * channels in each group than given by maxChInGrp. + * @param [in] The packed channel mode of the configuration to be processed. * @param [in] Array containing the channel mapping to be used (From MPEG PCE ordering to whatever is required). * @param [out] Array where corresponding channel types for each channels are stored into. * @param [out] Array where corresponding channel type indices for each output channel are stored into. * @param [out] Array where the buffer offsets for each channel are stored into. * @returns None. **/ +static void getChannelDescription ( const PCM_DMX_CHANNEL_MODE chMode, /* in */ - const UCHAR channelMapping[][PCM_DMX_MAX_CHANNELS], /* in */ + const UCHAR channelMapping[][8], /* in */ AUDIO_CHANNEL_TYPE channelType[], /* out */ UCHAR channelIndices[], /* out */ UCHAR offsetTable[PCM_DMX_MAX_CHANNELS] /* out */ @@ -412,15 +680,17 @@ void getChannelDescription ( FDK_ASSERT(offsetTable != NULL); /* Init output arrays */ - FDKmemclear(channelType, PCM_DMX_MAX_CHANNELS*sizeof(AUDIO_CHANNEL_TYPE)); - FDKmemclear(channelIndices, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR)); + FDKmemclear(channelType, PCM_DMX_MAX_IO_CHANNELS*sizeof(AUDIO_CHANNEL_TYPE)); + FDKmemclear(channelIndices, PCM_DMX_MAX_IO_CHANNELS*sizeof(UCHAR)); FDKmemset(offsetTable, 255, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR)); /* Extract the number of channels per group */ numChInGrp[CH_GROUP_FRONT] = chMode & 0xF; +#if (PCM_DMX_MAX_CHANNEL_GROUPS > 1) numChInGrp[CH_GROUP_SIDE] = (chMode >> 4) & 0xF; numChInGrp[CH_GROUP_REAR] = (chMode >> 8) & 0xF; numChInGrp[CH_GROUP_LFE] = (chMode >> 12) & 0xF; +#endif /* Summerize to get the total number of channels */ for (grpIdx = 0; grpIdx < PCM_DMX_MAX_CHANNEL_GROUPS; grpIdx += 1) { @@ -428,7 +698,29 @@ void getChannelDescription ( } /* Get the appropriate channel map */ - pChannelMap = channelMapping[numChannels-1]; + switch (chMode) { + case CH_MODE_1_0_0_0: + case CH_MODE_2_0_0_0: + case CH_MODE_3_0_0_0: + case CH_MODE_3_0_1_0: + case CH_MODE_3_0_2_0: + case CH_MODE_3_0_2_1: + pChannelMap = channelMapping[numChannels]; + break; + case CH_MODE_3_0_3_1: + pChannelMap = channelMapping[11]; + break; + case CH_MODE_3_0_4_1: + pChannelMap = channelMapping[12]; + break; + case CH_MODE_5_0_2_1: + pChannelMap = channelMapping[7]; + break; + default: + /* fallback */ + pChannelMap = channelMapping[0]; + break; + } /* Compose channel offset table */ @@ -436,15 +728,17 @@ void getChannelDescription ( if (numChInGrp[CH_GROUP_FRONT] & 0x1) { /* Odd number of front channels -> we have a center channel. In MPEG-4 the center has the index 0. */ - offsetTable[CENTER_FRONT_CHANNEL] = pChannelMap[0]; - channelType[0] = ACT_FRONT; + int mappedIdx = pChannelMap[ch]; + offsetTable[CENTER_FRONT_CHANNEL] = mappedIdx; + channelType[mappedIdx] = ACT_FRONT; + channelIndices[mappedIdx] = 0; ch += 1; } for (grpIdx = 0; grpIdx < PCM_DMX_MAX_CHANNEL_GROUPS; grpIdx += 1) { - AUDIO_CHANNEL_TYPE type; - int chMapPos, maxChannels = 0; - int chIdx = 0; + AUDIO_CHANNEL_TYPE type = ACT_NONE; + int chMapPos = 0, maxChannels = 0; + int chIdx = 0; /* Index of channel within the specific group */ switch (grpIdx) { case CH_GROUP_FRONT: @@ -453,10 +747,11 @@ void getChannelDescription ( maxChannels = 3; chIdx = numChInGrp[grpIdx] & 0x1; break; +#if (PCM_DMX_MAX_CHANNEL_GROUPS > 1) case CH_GROUP_SIDE: + /* Always map side channels to the multipurpose group. */ type = ACT_SIDE; - chMapPos = LEFT_OUTSIDE_CHANNEL; - maxChannels = 2; + chMapPos = LEFT_MULTIPRPS_CHANNEL; break; case CH_GROUP_REAR: type = ACT_BACK; @@ -468,20 +763,646 @@ void getChannelDescription ( chMapPos = LOW_FREQUENCY_CHANNEL; maxChannels = 1; break; +#endif default: break; } - for ( ; (chIdx < numChInGrp[grpIdx]) && (chIdx < maxChannels); chIdx += 1) { - offsetTable[chMapPos] = pChannelMap[ch]; - channelType[ch] = type; - channelIndices[ch] = chIdx; + /* Map all channels in this group */ + for ( ; chIdx < numChInGrp[grpIdx]; chIdx += 1) { + int mappedIdx = pChannelMap[ch]; + if (chIdx == maxChannels) { + /* No space left in this channel group! + Use the multipurpose group instead: */ + chMapPos = LEFT_MULTIPRPS_CHANNEL; + } + offsetTable[chMapPos] = mappedIdx; + channelType[mappedIdx] = type; + channelIndices[mappedIdx] = chIdx; chMapPos += 1; ch += 1; } } } +/** Private helper function for downmix matrix manipulation that initializes + * one row in a given downmix matrix (corresponding to one output channel). + * @param [inout] Pointer to fixed-point parts of the downmix matrix. + * @param [inout] Pointer to scale factor matrix associated to the downmix factors. + * @param [in] Index of channel (row) to be initialized. + * @returns Nothing to return. + **/ +static +void dmxInitChannel( + FIXP_DMX mixFactors[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS], + INT mixScales[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS], + const unsigned int outCh + ) +{ + unsigned int inCh; + for (inCh=0; inCh < PCM_DMX_MAX_CHANNELS; inCh+=1) { + if (inCh == outCh) { + mixFactors[outCh][inCh] = FL2FXCONST_DMX(0.5f); + mixScales[outCh][inCh] = 1; + } else { + mixFactors[outCh][inCh] = FL2FXCONST_DMX(0.0f); + mixScales[outCh][inCh] = 0; + } + } +} + +/** Private helper function for downmix matrix manipulation that does a reset + * of one row in a given downmix matrix (corresponding to one output channel). + * @param [inout] Pointer to fixed-point parts of the downmix matrix. + * @param [inout] Pointer to scale factor matrix associated to the downmix factors. + * @param [in] Index of channel (row) to be cleared/reset. + * @returns Nothing to return. + **/ +static +void dmxClearChannel( + FIXP_DMX mixFactors[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS], + INT mixScales[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS], + const unsigned int outCh + ) +{ + FDKmemclear(&mixFactors[outCh], PCM_DMX_MAX_CHANNELS*sizeof(FIXP_DMX)); + FDKmemclear(&mixScales[outCh], PCM_DMX_MAX_CHANNELS*sizeof(INT)); +} + +/** Private helper function for downmix matrix manipulation that applies a source channel (row) + * scaled by a given mix factor to a destination channel (row) in a given downmix matrix. + * Existing mix factors of the destination channel (row) will get overwritten. + * @param [inout] Pointer to fixed-point parts of the downmix matrix. + * @param [inout] Pointer to scale factor matrix associated to the downmix factors. + * @param [in] Index of source channel (row). + * @param [in] Index of destination channel (row). + * @param [in] Fixed-point part of mix factor to be applied. + * @param [in] Scale factor of mix factor to be applied. + * @returns Nothing to return. + **/ +static +void dmxSetChannel( + FIXP_DMX mixFactors[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS], + INT mixScales[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS], + const unsigned int dstCh, + const unsigned int srcCh, + const FIXP_DMX factor, + const INT scale + ) +{ + int ch; + for (ch=0; ch < PCM_DMX_MAX_CHANNELS; ch+=1) { + if (mixFactors[srcCh][ch] != (FIXP_DMX)0) { + mixFactors[dstCh][ch] = FX_DBL2FX_DMX(fMult(mixFactors[srcCh][ch], factor)); + mixScales[dstCh][ch] = mixScales[srcCh][ch] + scale; + } + } +} + +/** Private helper function for downmix matrix manipulation that adds a source channel (row) + * scaled by a given mix factor to a destination channel (row) in a given downmix matrix. + * @param [inout] Pointer to fixed-point parts of the downmix matrix. + * @param [inout] Pointer to scale factor matrix associated to the downmix factors. + * @param [in] Index of source channel (row). + * @param [in] Index of destination channel (row). + * @param [in] Fixed-point part of mix factor to be applied. + * @param [in] Scale factor of mix factor to be applied. + * @returns Nothing to return. + **/ +static +void dmxAddChannel( + FIXP_DMX mixFactors[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS], + INT mixScales[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS], + const unsigned int dstCh, + const unsigned int srcCh, + const FIXP_DMX factor, + const INT scale + ) +{ + int ch; + for (ch=0; ch < PCM_DMX_MAX_CHANNELS; ch+=1) { + FIXP_DBL addFact = fMult(mixFactors[srcCh][ch], factor); + if (addFact != (FIXP_DMX)0) { + INT newScale = mixScales[srcCh][ch] + scale; + if (mixFactors[dstCh][ch] != (FIXP_DMX)0) { + if (newScale > mixScales[dstCh][ch]) { + mixFactors[dstCh][ch] >>= newScale - mixScales[dstCh][ch]; + } else { + addFact >>= mixScales[dstCh][ch] - newScale; + newScale = mixScales[dstCh][ch]; + } + } + mixFactors[dstCh][ch] += FX_DBL2FX_DMX(addFact); + mixScales[dstCh][ch] = newScale; + } + } +} + + +/** Private function that creates a downmix factor matrix depending on the input and output + * configuration, the user parameters as well as the given metadata. This function is the modules + * brain and hold all downmix algorithms. + * @param [in] Flag that indicates if inChMode holds a real (packed) channel mode or has been + converted to a MPEG-4 channel configuration index. + * @param [in] Dependent on the inModeIsCfg flag this field hands in a (packed) channel mode or + the corresponding MPEG-4 channel configuration index.of the input configuration. + * @param [in] The (packed) channel mode of the output configuration. + * @param [in] Pointer to structure holding all current user parameter. + * @param [in] Pointer to field holding all current meta data. + * @param [out] Pointer to fixed-point parts of the downmix matrix. Normalized to one scale factor. + * @param [out] The common scale factor of the downmix matrix. + * @returns An error code. + **/ +static +PCMDMX_ERROR getMixFactors ( + const UCHAR inModeIsCfg, + PCM_DMX_CHANNEL_MODE inChMode, + const PCM_DMX_CHANNEL_MODE outChMode, + const PCM_DMX_USER_PARAMS *pParams, + const DMX_BS_META_DATA *pMetaData, + FIXP_DMX mixFactors[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS], + INT *pOutScale + ) +{ + PCMDMX_ERROR err = PCMDMX_OK; + INT mixScales[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS]; + INT maxScale = 0; + int numInChannel, numOutChannel; + unsigned int outCh, inCh, inChCfg = 0; + unsigned int valid[PCM_DMX_MAX_CHANNELS] = { 0 }; + + FDK_ASSERT(pMetaData != NULL); + FDK_ASSERT(mixFactors != NULL); + /* Check on a supported output configuration */ + FDK_ASSERT( (outChMode == CH_MODE_1_0_0_0) + || (outChMode == CH_MODE_2_0_0_0) + || (outChMode == CH_MODE_3_0_2_1) ); + + if (inModeIsCfg) { + /* Workaround for the ambiguity of the internal channel modes. + Convert channel config to channel mode: */ + inChCfg = (unsigned int)inChMode; + switch (inChCfg) { + case 1: case 2: case 3: +#if (PCM_DMX_MAX_CHANNELS > 3) + case 4: case 5: case 6: +#endif + inChMode = outChModeTable[inChCfg]; + break; + case 11: + inChMode = CH_MODE_3_0_3_1; + break; + case 12: + inChMode = CH_MODE_3_0_4_1; + break; + case 7: case 14: + inChMode = CH_MODE_5_0_2_1; + break; + default: + FDK_ASSERT(0); + } + } + + /* Extract the total number of input channels */ + numInChannel = (inChMode&0xF) + + ((inChMode>> 4)&0xF) + + ((inChMode>> 8)&0xF) + + ((inChMode>>12)&0xF); + /* Extract the total number of output channels */ + numOutChannel = (outChMode&0xF) + + ((outChMode>> 4)&0xF) + + ((outChMode>> 8)&0xF) + + ((outChMode>>12)&0xF); + + /* MPEG ammendment 4 aka ETSI metadata and fallback mode: */ + + + /* Create identity DMX matrix: */ + for (outCh=0; outCh < PCM_DMX_MAX_CHANNELS; outCh+=1) { + dmxInitChannel( mixFactors, mixScales, outCh ); + } + if (((inChMode>>12)&0xF) == 0) { + /* Clear empty or wrongly mapped input channel */ + dmxClearChannel( mixFactors, mixScales, LOW_FREQUENCY_CHANNEL ); + } + + /* FIRST STAGE: */ + if (numInChannel > SIX_CHANNEL) + { /* Always use MPEG equations either with meta data or with default values. */ + FIXP_DMX dMixFactA, dMixFactB; + INT dMixScaleA, dMixScaleB; + int isValidCfg = TRUE; + + /* Get factors from meta data */ + dMixFactA = abMixLvlValueTab[pMetaData->dmixIdxA]; + dMixScaleA = (pMetaData->dmixIdxA==0) ? 1 : 0; + dMixFactB = abMixLvlValueTab[pMetaData->dmixIdxB]; + dMixScaleB = (pMetaData->dmixIdxB==0) ? 1 : 0; + + /* Check if input is in the list of supported configurations */ + switch (inChMode) { + case CH_MODE_3_0_3_1: /* chCfg 11 */ + /* 6.1ch: C' = C; L' = L; R' = R; LFE' = LFE; + Ls' = Ls*dmix_a_idx + Cs*dmix_b_idx; + Rs' = Rs*dmix_a_idx + Cs*dmix_b_idx; */ + dmxClearChannel( mixFactors, mixScales, RIGHT_MULTIPRPS_CHANNEL ); /* clear empty input channel */ + dmxSetChannel( mixFactors, mixScales, LEFT_REAR_CHANNEL, LEFT_REAR_CHANNEL, dMixFactA, dMixScaleA ); + dmxSetChannel( mixFactors, mixScales, LEFT_REAR_CHANNEL, LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB ); + dmxSetChannel( mixFactors, mixScales, RIGHT_REAR_CHANNEL, RIGHT_REAR_CHANNEL, dMixFactA, dMixScaleA ); + dmxSetChannel( mixFactors, mixScales, RIGHT_REAR_CHANNEL, LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB ); + break; + case CH_MODE_3_2_1_0: + case CH_MODE_3_2_1_1: /* chCfg 11 but with side channels */ + /* 6.1ch: C' = C; L' = L; R' = R; LFE' = LFE; + Ls' = Ls*dmix_a_idx + Cs*dmix_b_idx; + Rs' = Rs*dmix_a_idx + Cs*dmix_b_idx; */ + dmxClearChannel( mixFactors, mixScales, RIGHT_REAR_CHANNEL ); /* clear empty input channel */ + dmxSetChannel( mixFactors, mixScales, RIGHT_REAR_CHANNEL, LEFT_REAR_CHANNEL, dMixFactB, dMixScaleB ); + dmxSetChannel( mixFactors, mixScales, RIGHT_REAR_CHANNEL, RIGHT_MULTIPRPS_CHANNEL, dMixFactA, dMixScaleA ); + dmxSetChannel( mixFactors, mixScales, LEFT_REAR_CHANNEL, LEFT_REAR_CHANNEL, dMixFactB, dMixScaleB ); + dmxSetChannel( mixFactors, mixScales, LEFT_REAR_CHANNEL, LEFT_MULTIPRPS_CHANNEL, dMixFactA, dMixScaleA ); + isValidCfg = FALSE; + err = PCMDMX_INVALID_MODE; + break; + case CH_MODE_5_2_1_0: + case CH_MODE_5_0_1_0: + case CH_MODE_5_0_1_1: + /* Ls' = Cs*dmix_a_idx; + Rs' = Cs*dmix_a_idx; */ + dmxClearChannel( mixFactors, mixScales, RIGHT_REAR_CHANNEL ); /* clear empty input channel */ + dmxSetChannel( mixFactors, mixScales, RIGHT_REAR_CHANNEL, LEFT_REAR_CHANNEL, dMixFactA, dMixScaleA ); + dmxSetChannel( mixFactors, mixScales, LEFT_REAR_CHANNEL, LEFT_REAR_CHANNEL, dMixFactA, dMixScaleA ); + isValidCfg = FALSE; + err = PCMDMX_INVALID_MODE; + break; + case CH_MODE_3_0_4_1: /* chCfg 12 */ + /* 7.1ch Surround Back: C' = C; L' = L; R' = R; LFE' = LFE; + Ls' = Ls*dmix_a_idx + Lsr*dmix_b_idx; + Rs' = Rs*dmix_a_idx + Rsr*dmix_b_idx; */ + dmxSetChannel( mixFactors, mixScales, LEFT_REAR_CHANNEL, LEFT_REAR_CHANNEL, dMixFactA, dMixScaleA ); + dmxSetChannel( mixFactors, mixScales, LEFT_REAR_CHANNEL, LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB ); + dmxSetChannel( mixFactors, mixScales, RIGHT_REAR_CHANNEL, RIGHT_REAR_CHANNEL, dMixFactA, dMixScaleA ); + dmxSetChannel( mixFactors, mixScales, RIGHT_REAR_CHANNEL, RIGHT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB ); + break; + case CH_MODE_5_0_2_1: /* chCfg 7 || 14 */ + if (inChCfg == 14) { + /* 7.1ch Front Height: C' = C; Ls' = Ls; Rs' = Rs; LFE' = LFE; + L' = L*dmix_a_idx + Lv*dmix_b_idx; + R' = R*dmix_a_idx + Rv*dmix_b_idx; */ + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, dMixFactA, dMixScaleA ); + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, dMixFactA, dMixScaleA ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB ); + } else { + /* 7.1ch Front: Ls' = Ls; Rs' = Rs; LFE' = LFE; + C' = C + (Lc+Rc)*dmix_a_idx; + L' = L + Lc*dmix_b_idx; + R' = R + Rc*dmix_b_idx; + CAUTION: L+R are not at (MPEG) index 1+2. */ + dmxSetChannel( mixFactors, mixScales, CENTER_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, dMixFactA, dMixScaleA ); + dmxSetChannel( mixFactors, mixScales, CENTER_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, dMixFactA, dMixScaleA ); + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, dMixFactB, dMixScaleB ); + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_MULTIPRPS_CHANNEL, FL2FXCONST_DMX(0.5f), 1 ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, dMixFactB, dMixScaleB ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_MULTIPRPS_CHANNEL, FL2FXCONST_DMX(0.5f), 1 ); + } + break; + default: + /* Nothing to do. Just use the identity matrix. */ + isValidCfg = FALSE; + err = PCMDMX_INVALID_MODE; + break; + } + + /* Add additional DMX gain */ + if ( (isValidCfg == TRUE) + && (pMetaData->dmxGainIdx5 != 0)) + { /* Apply DMX gain 5 */ + FIXP_DMX dmxGain; + INT dmxScale; + INT sign = (pMetaData->dmxGainIdx5 & 0x40) ? -1 : 1; + INT val = pMetaData->dmxGainIdx5 & 0x3F; + + /* 10^(dmx_gain_5/80) */ + dmxGain = FX_DBL2FX_DMX( fLdPow( + FL2FXCONST_DBL(0.830482023721841f), 2, /* log2(10) */ + (FIXP_DBL)(sign*val*(LONG)FL2FXCONST_DBL(0.0125f)), 0, + &dmxScale ) + ); + /* Currently only positive scale factors supported! */ + if (dmxScale < 0) { + dmxGain >>= -dmxScale; + dmxScale = 0; + } + + dmxSetChannel( mixFactors, mixScales, CENTER_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, dmxGain, dmxScale ); + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, dmxGain, dmxScale ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, dmxGain, dmxScale ); + dmxSetChannel( mixFactors, mixScales, LEFT_REAR_CHANNEL, LEFT_REAR_CHANNEL, dmxGain, dmxScale ); + dmxSetChannel( mixFactors, mixScales, RIGHT_REAR_CHANNEL, RIGHT_REAR_CHANNEL, dmxGain, dmxScale ); + dmxSetChannel( mixFactors, mixScales, LOW_FREQUENCY_CHANNEL, LOW_FREQUENCY_CHANNEL, dmxGain, dmxScale ); + } + + /* Mark the output channels */ + valid[CENTER_FRONT_CHANNEL] = 1; + valid[LEFT_FRONT_CHANNEL] = 1; + valid[RIGHT_FRONT_CHANNEL] = 1; + valid[LEFT_REAR_CHANNEL] = 1; + valid[RIGHT_REAR_CHANNEL] = 1; + valid[LOW_FREQUENCY_CHANNEL] = 1; + + /* Update channel mode for the next stage */ + inChMode = CH_MODE_3_0_2_1; + } + + /* SECOND STAGE: */ + if (numOutChannel <= TWO_CHANNEL) { + /* Create DMX matrix according to input configuration */ + switch (inChMode) { + case CH_MODE_2_0_0_0: /* chCfg 2 */ + /* Apply the dual channel mode. */ + switch (pParams->dualChannelMode) { + case CH1_MODE: /* L' = 0.707 * Ch1; + R' = 0.707 * Ch1; */ + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + break; + case CH2_MODE: /* L' = 0.707 * Ch2; + R' = 0.707 * Ch2; */ + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + break; + case MIXED_MODE: /* L' = 0.5*Ch1 + 0.5*Ch2; + R' = 0.5*Ch1 + 0.5*Ch2; */ + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0 ); + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0 ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0 ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0 ); + break; + default: + case STEREO_MODE: + /* Nothing to do */ + break; + } + break; + case CH_MODE_2_0_1_0: + /* L' = L + 0.707*S; + R' = R + 0.707*S; */ + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_REAR_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, LEFT_REAR_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + break; + case CH_MODE_3_0_0_0: /* chCfg 3 */ + /* L' = L + 0.707*C; + R' = R + 0.707*C; */ + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + break; + case CH_MODE_3_0_1_0: /* chCfg 4 */ + /* L' = L + 0.707*C + 0.707*S; + R' = R + 0.707*C + 0.707*S; */ + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_REAR_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, LEFT_REAR_CHANNEL, FL2FXCONST_DMX(0.707f), 0 ); + break; + case CH_MODE_3_0_2_0: /* chCfg 5 */ + case CH_MODE_3_0_2_1: /* chCfg 6 */ + /* MPEG + ITU + DLB + But because the default downmix equations and coefficients are equal we stick to MPEG. */ + if ( (pMetaData->typeFlags & TYPE_DSE_DATA) + || !(pMetaData->typeFlags & TYPE_PCE_DATA) ) + { + FIXP_DMX cMixLvl, sMixLvl, lMixLvl; + INT cMixScale, sMixScale, lMixScale; + + /* Get factors from meta data */ + cMixLvl = abMixLvlValueTab[pMetaData->cLevIdx]; + cMixScale = (pMetaData->cLevIdx==0) ? 1 : 0; + sMixLvl = abMixLvlValueTab[pMetaData->sLevIdx]; + sMixScale = (pMetaData->sLevIdx==0) ? 1 : 0; + lMixLvl = lfeMixLvlValueTab[pMetaData->dmixIdxLfe]; + if (pMetaData->dmixIdxLfe <= 1) { + lMixScale = 2; + } else if (pMetaData->dmixIdxLfe <= 5) { + lMixScale = 1; + } else { + lMixScale = 0; + } + /* Setup the DMX matrix */ + if ( (pParams->pseudoSurrMode == FORCE_PS_DMX) + || ((pParams->pseudoSurrMode == AUTO_PS_DMX) && (pMetaData->pseudoSurround==1))) + { /* L' = L + C*clev - (Ls+Rs)*slev + LFE*lflev; + R' = R + C*clev + (Ls+Rs)*slev + LFE*lflev; */ + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, cMixLvl, cMixScale ); + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_REAR_CHANNEL, -sMixLvl, sMixScale ); + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, RIGHT_REAR_CHANNEL, -sMixLvl, sMixScale ); + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, cMixLvl, cMixScale ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, LEFT_REAR_CHANNEL, sMixLvl, sMixScale ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_REAR_CHANNEL, sMixLvl, sMixScale ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale ); + } + else + { /* L' = L + C*clev + Ls*slev + LFE*llev; + R' = R + C*clev + Rs*slev + LFE*llev; */ + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, cMixLvl, cMixScale ); + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_REAR_CHANNEL, sMixLvl, sMixScale ); + dmxAddChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, cMixLvl, cMixScale ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_REAR_CHANNEL, sMixLvl, sMixScale ); + dmxAddChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale ); + } + + /* Add additional DMX gain */ + if ( pMetaData->dmxGainIdx2 != 0 ) + { /* Apply DMX gain 2 */ + FIXP_DMX dmxGain; + INT dmxScale; + INT sign = (pMetaData->dmxGainIdx2 & 0x40) ? -1 : 1; + INT val = pMetaData->dmxGainIdx2 & 0x3F; + + /* 10^(dmx_gain_2/80) */ + dmxGain = FX_DBL2FX_DMX( fLdPow( + FL2FXCONST_DBL(0.830482023721841f), 2, /* log2(10) */ + (FIXP_DBL)(sign*val*(LONG)FL2FXCONST_DBL(0.0125f)), 0, + &dmxScale ) + ); + /* Currently only positive scale factors supported! */ + if (dmxScale < 0) { + dmxGain >>= -dmxScale; + dmxScale = 0; + } + + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, dmxGain, dmxScale ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, dmxGain, dmxScale ); + } + } +#ifdef PCE_METADATA_ENABLE + else { + FIXP_DMX flev, clev, slevLL, slevLR, slevRL, slevRR; + FIXP_DMX mtrxMixDwnCoef = mpegMixDownIdx2Coef[pMetaData->matrixMixdownIdx]; + + if ( (pParams->pseudoSurrMode == FORCE_PS_DMX) + || ((pParams->pseudoSurrMode == AUTO_PS_DMX) && (pMetaData->pseudoSurround==1))) + { /* 3/2 input: L' = (1.707+2*A)^-1 * [L+0.707*C-A*Ls-A*Rs]; + R' = (1.707+2*A)^-1 * [R+0.707*C+A*Ls+A*Rs]; */ + flev = mpegMixDownIdx2PreFact[1][pMetaData->matrixMixdownIdx]; + slevRR = slevRL = FX_DBL2FX_DMX(fMult(flev, mtrxMixDwnCoef)); + slevLL = slevLR = -slevRL; + } + else { + /* 3/2 input: L' = (1.707+A)^-1 * [L+0.707*C+A*Ls]; + R' = (1.707+A)^-1 * [R+0.707*C+A*Rs]; */ + flev = mpegMixDownIdx2PreFact[0][pMetaData->matrixMixdownIdx]; + slevRR = slevLL = FX_DBL2FX_DMX(fMult(flev, mtrxMixDwnCoef)); + slevLR = slevRL = (FIXP_SGL)0; + } + /* common factor */ + clev = FX_DBL2FX_DMX(fMult(flev, mpegMixDownIdx2Coef[0] /* 0.707 */)); + + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, flev, 0 ); + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, clev, 0 ); + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, LEFT_REAR_CHANNEL, slevLL, 0 ); + dmxSetChannel( mixFactors, mixScales, LEFT_FRONT_CHANNEL, RIGHT_REAR_CHANNEL, slevLR, 0 ); + + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, flev, 0 ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, CENTER_FRONT_CHANNEL, clev, 0 ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, LEFT_REAR_CHANNEL, slevRL, 0 ); + dmxSetChannel( mixFactors, mixScales, RIGHT_FRONT_CHANNEL, RIGHT_REAR_CHANNEL, slevRR, 0 ); + } +#endif /* PCE_METADATA_ENABLE */ + break; + default: + /* This configuration does not fit to any known downmix equation! */ + err = PCMDMX_INVALID_MODE; + break; + } + /* Mark the output channels */ + FDKmemclear(valid, PCM_DMX_MAX_CHANNELS*sizeof(unsigned int)); + valid[LEFT_FRONT_CHANNEL] = 1; + valid[RIGHT_FRONT_CHANNEL] = 1; + /* Update channel mode for the next stage */ + inChMode = CH_MODE_2_0_0_0; + } + + if (numOutChannel == ONE_CHANNEL) { + FIXP_DMX monoMixLevel; + INT monoMixScale; + +#ifdef PCE_METADATA_ENABLE + if ( (pMetaData->typeFlags & TYPE_PCE_DATA) + && !(pMetaData->typeFlags & TYPE_DSE_DATA) ) + { /* C' = (3+2*A)^-1 * [C+L+R+A*Ls+A+Rs]; */ + monoMixLevel = mpegMixDownIdx2PreFact[2][pMetaData->matrixMixdownIdx]; + monoMixScale = 0; + + dmxClearChannel( mixFactors, mixScales, CENTER_FRONT_CHANNEL ); + mixFactors[CENTER_FRONT_CHANNEL][CENTER_FRONT_CHANNEL] = monoMixLevel; + mixFactors[CENTER_FRONT_CHANNEL][LEFT_FRONT_CHANNEL] = monoMixLevel; + mixFactors[CENTER_FRONT_CHANNEL][RIGHT_FRONT_CHANNEL] = monoMixLevel; + monoMixLevel = FX_DBL2FX_DMX(fMult(monoMixLevel, mpegMixDownIdx2Coef[pMetaData->matrixMixdownIdx])); + mixFactors[CENTER_FRONT_CHANNEL][LEFT_REAR_CHANNEL] = monoMixLevel; + mixFactors[CENTER_FRONT_CHANNEL][RIGHT_REAR_CHANNEL] = monoMixLevel; + } + else +#endif + { /* C' = L + R; [default] */ + monoMixLevel = FL2FXCONST_DMX(0.5f); + monoMixScale = 1; + dmxClearChannel( mixFactors, mixScales, CENTER_FRONT_CHANNEL ); /* C is not in the mix */ + dmxSetChannel( mixFactors, mixScales, CENTER_FRONT_CHANNEL, LEFT_FRONT_CHANNEL, monoMixLevel, monoMixScale ); + dmxAddChannel( mixFactors, mixScales, CENTER_FRONT_CHANNEL, RIGHT_FRONT_CHANNEL, monoMixLevel, monoMixScale ); + } + + /* Mark the output channel */ + FDKmemclear(valid, PCM_DMX_MAX_CHANNELS*sizeof(unsigned int)); + valid[CENTER_FRONT_CHANNEL] = 1; + } + +#define MAX_SEARCH_START_VAL ( -7 ) + + { + LONG chSum[PCM_DMX_MAX_CHANNELS]; + INT chSumMax = MAX_SEARCH_START_VAL; + + /* Determine the current maximum scale factor */ + for (outCh=0; outCh < PCM_DMX_MAX_CHANNELS; outCh+=1) { + if (valid[outCh]!=0) { + for (inCh=0; inCh < PCM_DMX_MAX_CHANNELS; inCh+=1) { + if (mixScales[outCh][inCh] > maxScale) + { /* Store the new maximum */ + maxScale = mixScales[outCh][inCh]; + } + } + } + } + + /* Individualy analyse output chanal levels */ + for (outCh=0; outCh < PCM_DMX_MAX_CHANNELS; outCh+=1) { + chSum[outCh] = MAX_SEARCH_START_VAL; + if (valid[outCh]!=0) { + int ovrflwProtScale = 0; + + /* Accumulate all factors for each output channel */ + chSum[outCh] = 0; + for (inCh=0; inCh < PCM_DMX_MAX_CHANNELS; inCh+=1) { + SHORT addFact = FX_DMX2SHRT(mixFactors[outCh][inCh]); + if ( mixScales[outCh][inCh] <= maxScale ) { + addFact >>= maxScale - mixScales[outCh][inCh]; + } else { + addFact <<= mixScales[outCh][inCh] - maxScale; + } + chSum[outCh] += addFact; + } + if (chSum[outCh] > (LONG)MAXVAL_SGL) { + while (chSum[outCh] > (LONG)MAXVAL_SGL) { + ovrflwProtScale += 1; + chSum[outCh] >>= 1; + } + } else if (chSum[outCh] > 0) { + while ((chSum[outCh]<<1) <= (LONG)MAXVAL_SGL) { + ovrflwProtScale -= 1; + chSum[outCh] <<= 1; + } + } + /* Store the differential scaling in the same array */ + chSum[outCh] = ovrflwProtScale; + } + } + + for (outCh=0; outCh < PCM_DMX_MAX_CHANNELS; outCh+=1) { + if ( (valid[outCh] != 0) + && (chSum[outCh] > chSumMax) ) + { /* Store the new maximum */ + chSumMax = chSum[outCh]; + } + } + maxScale = FDKmax(maxScale+chSumMax, 0); + + /* Normalize all factors */ + for (outCh=0; outCh < PCM_DMX_MAX_CHANNELS; outCh+=1) { + if (valid[outCh]!=0) { + for (inCh=0; inCh < PCM_DMX_MAX_CHANNELS; inCh+=1) { + if (mixFactors[outCh][inCh] != (FIXP_DMX)0) { + if ( mixScales[outCh][inCh] <= maxScale ) { + mixFactors[outCh][inCh] >>= maxScale - mixScales[outCh][inCh]; + } else { + mixFactors[outCh][inCh] <<= mixScales[outCh][inCh] - maxScale; + } + mixScales[outCh][inCh] = maxScale; + } + } + } + } + } + + + /* return the scale factor */ + *pOutScale = maxScale; + + return (err); +} + /** Open and initialize an instance of the PCM downmix module * @param [out] Pointer to a buffer receiving the handle of the new instance. @@ -526,24 +1447,24 @@ PCMDMX_ERROR pcmDmx_Reset ( if (self == NULL) { return (PCMDMX_INVALID_HANDLE); } if (flags & PCMDMX_RESET_PARAMS) { - self->dualChannelMode = STEREO_MODE; - self->numOutputChannels = 0; - self->applyProcessing = 0; - self->frameDelay = 0; - self->expiryFrame = PCMDMX_DFLT_EXPIRY_FRAME; + PCM_DMX_USER_PARAMS *pParams = &self->userParams; + + pParams->dualChannelMode = STEREO_MODE; + pParams->pseudoSurrMode = NEVER_DO_PS_DMX; + pParams->numOutChannelsMax = PCM_DMX_DFLT_MAX_OUT_CHANNELS; + pParams->numOutChannelsMin = PCM_DMX_DFLT_MIN_OUT_CHANNELS; + pParams->frameDelay = 0; + pParams->expiryFrame = PCM_DMX_DFLT_EXPIRY_FRAME; + + self->applyProcessing = 0; } if (flags & PCMDMX_RESET_BS_DATA) { int slot; + /* Init all slots with a default set */ for (slot = 0; slot <= PCM_DMX_MAX_DELAY_FRAMES; slot += 1) { - self->dvbMixDownLevels[slot].centerMixLevelValue = dvbDownmixFactors[2]; /* 0.707 */ - self->dvbMixDownLevels[slot].surroundMixLevelValue = dvbDownmixFactors[0]; /* 1.000 */ - self->dvbMixDownLevels[slot].mixLevelsAvail = 0; - - self->mpegMixDownInfo[slot].mixdownAvailable = 0; + FDKmemcpy(&self->bsMetaData[slot], &dfltMetaData, sizeof(DMX_BS_META_DATA)); } - /* Reset expiry counter */ - self->expiryCount = 0; } return (PCMDMX_OK); @@ -558,8 +1479,8 @@ PCMDMX_ERROR pcmDmx_Reset ( **/ PCMDMX_ERROR pcmDmx_SetParam ( HANDLE_PCM_DOWNMIX self, - PCMDMX_PARAM param, - UINT value + const PCMDMX_PARAM param, + const INT value ) { switch (param) @@ -567,39 +1488,70 @@ PCMDMX_ERROR pcmDmx_SetParam ( case DMX_BS_DATA_EXPIRY_FRAME: if (self == NULL) return (PCMDMX_INVALID_HANDLE); - self->expiryFrame = value; + self->userParams.expiryFrame = (value > 0) ? (UINT)value : 0; break; case DMX_BS_DATA_DELAY: - if (value > PCM_DMX_MAX_DELAY_FRAMES) { + if ( (value > PCM_DMX_MAX_DELAY_FRAMES) + || (value < 0) ) { return (PCMDMX_UNABLE_TO_SET_PARAM); } if (self == NULL) { return (PCMDMX_INVALID_HANDLE); } - self->frameDelay = value; + self->userParams.frameDelay = (UCHAR)value; break; - case NUMBER_OF_OUTPUT_CHANNELS: - switch ((int)value) { /* supported output channels */ - case -1: case 0: case 1: case 2: - case 6: case 8: + case MIN_NUMBER_OF_OUTPUT_CHANNELS: + switch (value) { /* supported output channels */ + case -1: case 0: case ONE_CHANNEL: case TWO_CHANNEL: +#if (PCM_DMX_MAX_OUT_CHANNELS >= 6) + case SIX_CHANNEL: +#endif +#if (PCM_DMX_MAX_OUT_CHANNELS >= 8) + case EIGHT_CHANNEL: +#endif break; default: return (PCMDMX_UNABLE_TO_SET_PARAM); } if (self == NULL) return (PCMDMX_INVALID_HANDLE); - if ((int)value > 0) { - self->numOutputChannels = (int)value; - self->applyProcessing = 1; - } else { - self->numOutputChannels = 0; - self->applyProcessing = 0; + /* Store the new value */ + self->userParams.numOutChannelsMin = (value > 0) ? value : -1; + if ( (value > 0) + && (self->userParams.numOutChannelsMax > 0) + && (value > self->userParams.numOutChannelsMax) ) + { /* MIN > MAX would be an invalid state. Thus set MAX = MIN in this case. */ + self->userParams.numOutChannelsMax = self->userParams.numOutChannelsMin; } break; - case DUAL_CHANNEL_DOWNMIX_MODE: + case MAX_NUMBER_OF_OUTPUT_CHANNELS: + switch (value) { /* supported output channels */ + case -1: case 0: case ONE_CHANNEL: case TWO_CHANNEL: +#if (PCM_DMX_MAX_OUT_CHANNELS >= 6) + case SIX_CHANNEL: +#endif +#if (PCM_DMX_MAX_OUT_CHANNELS >= 8) + case EIGHT_CHANNEL: +#endif + break; + default: + return (PCMDMX_UNABLE_TO_SET_PARAM); + } + if (self == NULL) + return (PCMDMX_INVALID_HANDLE); + /* Store the new value */ + self->userParams.numOutChannelsMax = (value > 0) ? value : -1; + if ( (value > 0) + && (value < self->userParams.numOutChannelsMin) ) + { /* MAX < MIN would be an invalid state. Thus set MIN = MAX in this case. */ + self->userParams.numOutChannelsMin = self->userParams.numOutChannelsMax; + } + break; + + case DMX_DUAL_CHANNEL_MODE: switch ((DUAL_CHANNEL_MODE)value) { case STEREO_MODE: case CH1_MODE: @@ -611,8 +1563,22 @@ PCMDMX_ERROR pcmDmx_SetParam ( } if (self == NULL) return (PCMDMX_INVALID_HANDLE); - self->dualChannelMode = (DUAL_CHANNEL_MODE)value; - self->applyProcessing = 1; + self->userParams.dualChannelMode = (DUAL_CHANNEL_MODE)value; + self->applyProcessing = 1; /* Force processing */ + break; + + case DMX_PSEUDO_SURROUND_MODE: + switch ((PSEUDO_SURROUND_MODE)value) { + case NEVER_DO_PS_DMX: + case AUTO_PS_DMX: + case FORCE_PS_DMX: + break; + default: + return (PCMDMX_UNABLE_TO_SET_PARAM); + } + if (self == NULL) + return (PCMDMX_INVALID_HANDLE); + self->userParams.pseudoSurrMode = (PSEUDO_SURROUND_MODE)value; break; default: @@ -622,87 +1588,239 @@ PCMDMX_ERROR pcmDmx_SetParam ( return (PCMDMX_OK); } - -/** Read the ancillary data transported in DSEs of DVB streams with MPEG-4 content +/** Get one parameter value of one PCM downmix module instance. * @param [in] Handle of PCM downmix module instance. - * @param [in] Pointer to ancillary data buffer. - * @param [in] Size of ancillary data. - * @param [in] Flag indicating wheter the DVB ancillary data is from an MPEG-1/2 or an MPEG-4 stream. + * @param [in] Parameter to be set. + * @param [out] Pointer to buffer receiving the parameter value. * @returns Returns an error code. **/ -PCMDMX_ERROR pcmDmx_ReadDvbAncData ( +PCMDMX_ERROR pcmDmx_GetParam ( HANDLE_PCM_DOWNMIX self, - UCHAR *pAncDataBuf, - UINT ancDataBytes, + const PCMDMX_PARAM param, + INT * const pValue + ) +{ + PCM_DMX_USER_PARAMS *pUsrParams; + + if ( (self == NULL) + || (pValue == NULL) ) { + return (PCMDMX_INVALID_HANDLE); + } + pUsrParams = &self->userParams; + + switch (param) + { + case DMX_BS_DATA_EXPIRY_FRAME: + *pValue = (INT)pUsrParams->expiryFrame; + break; + case DMX_BS_DATA_DELAY: + *pValue = (INT)pUsrParams->frameDelay; + break; + case MIN_NUMBER_OF_OUTPUT_CHANNELS: + *pValue = (INT)pUsrParams->numOutChannelsMin; + break; + case MAX_NUMBER_OF_OUTPUT_CHANNELS: + *pValue = (INT)pUsrParams->numOutChannelsMax; + break; + case DMX_DUAL_CHANNEL_MODE: + *pValue = (INT)pUsrParams->dualChannelMode; + break; + case DMX_PSEUDO_SURROUND_MODE: + *pValue = (INT)pUsrParams->pseudoSurrMode; + break; + default: + return (PCMDMX_UNKNOWN_PARAM); + } + + return (PCMDMX_OK); +} + + +#ifdef DSE_METADATA_ENABLE + +#define MAX_DSE_ANC_BYTES ( 16 ) /* 15 bytes */ +#define ANC_DATA_SYNC_BYTE ( 0xBC ) /* ancillary data sync byte. */ + +/* + * Read DMX meta-data from a data stream element. + */ +PCMDMX_ERROR pcmDmx_Parse ( + HANDLE_PCM_DOWNMIX self, + HANDLE_FDK_BITSTREAM hBs, + UINT ancDataBits, int isMpeg2 ) { - DVB_MIXDOWN_LEVELS *pDownmixLevels = &self->dvbMixDownLevels[0]; + PCMDMX_ERROR errorStatus = PCMDMX_OK; + DMX_BS_META_DATA *pBsMetaData = &self->bsMetaData[0]; - int offset = (isMpeg2) ? 2 : 0; - UCHAR ancDataStatus; + int skip4Dmx = 0, skip4Ext = 0; + int dmxLvlAvail = 0, extDataAvail = 0; + int foundNewData = 0; + UINT minAncBits = ((isMpeg2) ? 5 : 3)*8; - if (self == NULL) { return (PCMDMX_INVALID_HANDLE); } + if ( (self == NULL) + || (hBs == NULL) ) { return (PCMDMX_INVALID_HANDLE); } + + ancDataBits = FDKgetValidBits(hBs); /* sanity checks */ - if (pAncDataBuf == NULL || ancDataBytes < (UCHAR)(3+offset)) { + if ( (ancDataBits < minAncBits) + || (ancDataBits > FDKgetValidBits(hBs)) ) { return (PCMDMX_CORRUPT_ANC_DATA); } + pBsMetaData = &self->bsMetaData[0]; + + if (isMpeg2) { + /* skip DVD ancillary data */ + FDKpushFor(hBs, 16); + } + /* check sync word */ - if (pAncDataBuf[offset] != ANC_DATA_SYNC_BYTE) { + if (FDKreadBits(hBs,8) != ANC_DATA_SYNC_BYTE) { return (PCMDMX_CORRUPT_ANC_DATA); } - offset += 2; - ancDataStatus = pAncDataBuf[offset++]; + /* skip MPEG audio type and Dolby surround mode */ + FDKpushFor(hBs, 4); if (isMpeg2) { - /* skip advanced_dynamic_range_control */ - if (ancDataStatus & 0x80) offset += 3; - /* skip dialog_normalization */ - if (ancDataStatus & 0x40) offset += 1; - /* skip reproduction_level */ - if (ancDataStatus & 0x20) offset += 1; + /* int numAncBytes = */ FDKreadBits(hBs, 4); + /* advanced dynamic range control */ + if (FDKreadBit(hBs)) skip4Dmx += 24; + /* dialog normalization */ + if (FDKreadBit(hBs)) skip4Dmx += 8; + /* reproduction_level */ + if (FDKreadBit(hBs)) skip4Dmx += 8; + } else { + FDKpushFor(hBs, 2); /* drc presentation mode */ + pBsMetaData->pseudoSurround = FDKreadBit(hBs); + FDKpushFor(hBs, 4); /* reserved bits */ } - else { - /* check reserved bits */ - if (ancDataStatus & 0xE8) { return (PCMDMX_CORRUPT_ANC_DATA); } + + /* downmixing levels MPEGx status */ + dmxLvlAvail = FDKreadBit(hBs); + + if (isMpeg2) { + /* scale factor CRC status */ + if (FDKreadBit(hBs)) skip4Ext += 16; + } else { + /* ancillary data extension status */ + extDataAvail = FDKreadBit(hBs); } + /* audio coding and compression status */ + if (FDKreadBit(hBs)) skip4Ext += 16; + /* coarse grain timecode status */ + if (FDKreadBit(hBs)) skip4Ext += 16; + /* fine grain timecode status */ + if (FDKreadBit(hBs)) skip4Ext += 16; + + /* skip the useless data to get to the DMX levels */ + FDKpushFor(hBs, skip4Dmx); + /* downmix_levels_MPEGX */ - if (ancDataStatus & 0x10) + if (dmxLvlAvail) { - int foundNewData = 0; - UCHAR downmixData = pAncDataBuf[offset++]; - - if (downmixData & 0x80) { /* center_mix_level_on */ - pDownmixLevels->centerMixLevelValue = - dvbDownmixFactors[(downmixData >> 4) & 0x07]; + if (FDKreadBit(hBs)) { /* center_mix_level_on */ + pBsMetaData->cLevIdx = FDKreadBits(hBs, 3); foundNewData = 1; } else { - pDownmixLevels->centerMixLevelValue = dvbDownmixFactors[0]; - if (downmixData & 0x70) { return (PCMDMX_CORRUPT_ANC_DATA); } + FDKreadBits(hBs, 3); } - - if (downmixData & 0x08) { /* surround_mix_level_on */ - pDownmixLevels->surroundMixLevelValue = - dvbDownmixFactors[downmixData & 0x07]; + if (FDKreadBit(hBs)) { /* surround_mix_level_on */ + pBsMetaData->sLevIdx = FDKreadBits(hBs, 3); foundNewData = 1; } else { - pDownmixLevels->surroundMixLevelValue = dvbDownmixFactors[0]; - if (downmixData & 0x07) { return (PCMDMX_CORRUPT_ANC_DATA); } + FDKreadBits(hBs, 3); + } + } + + /* skip the useless data to get to the ancillary data extension */ + FDKpushFor(hBs, skip4Ext); + + /* anc data extension (MPEG-4 only) */ + if (extDataAvail) { + int extDmxLvlSt, extDmxGainSt, extDmxLfeSt; + + FDKreadBit(hBs); /* reserved bit */ + extDmxLvlSt = FDKreadBit(hBs); + extDmxGainSt = FDKreadBit(hBs); + extDmxLfeSt = FDKreadBit(hBs); + FDKreadBits(hBs, 4); /* reserved bits */ + + if (extDmxLvlSt) { + pBsMetaData->dmixIdxA = FDKreadBits(hBs, 3); + pBsMetaData->dmixIdxB = FDKreadBits(hBs, 3); + FDKreadBits(hBs, 2); /* reserved bits */ + foundNewData = 1; + } + if (extDmxGainSt) { + pBsMetaData->dmxGainIdx5 = FDKreadBits(hBs, 7); + FDKreadBit(hBs); /* reserved bit */ + pBsMetaData->dmxGainIdx2 = FDKreadBits(hBs, 7); + FDKreadBit(hBs); /* reserved bit */ + foundNewData = 1; + } + if (extDmxLfeSt) { + pBsMetaData->dmixIdxLfe = FDKreadBits(hBs, 4); + FDKreadBits(hBs, 4); /* reserved bits */ + foundNewData = 1; } + } - pDownmixLevels->mixLevelsAvail = foundNewData; + /* final sanity check on the amount of read data */ + if ((INT)FDKgetValidBits(hBs) < 0) { + errorStatus = PCMDMX_CORRUPT_ANC_DATA; } - /* Reset expiry counter */ - self->expiryCount = 0; + if ( (errorStatus == PCMDMX_OK) + && (foundNewData == 1) ) { + /* announce new data */ + pBsMetaData->typeFlags |= TYPE_DSE_DATA; + /* reset expiry counter */ + pBsMetaData->expiryCount = 0; + } - return (PCMDMX_OK); + return (errorStatus); +} + +/* + * Read DMX meta-data from a data stream element. + */ +PCMDMX_ERROR pcmDmx_ReadDvbAncData ( + HANDLE_PCM_DOWNMIX self, + UCHAR *pAncDataBuf, + UINT ancDataBytes, + int isMpeg2 + ) +{ + FDK_BITSTREAM bs; + HANDLE_FDK_BITSTREAM hBs = &bs; + PCMDMX_ERROR errorStatus = PCMDMX_OK; + + if (self == NULL) { return (PCMDMX_INVALID_HANDLE); } + + /* sanity checks */ + if ( (pAncDataBuf == NULL) + || (ancDataBytes == 0) ) { + return (PCMDMX_CORRUPT_ANC_DATA); + } + + FDKinitBitStream (hBs, pAncDataBuf, MAX_DSE_ANC_BYTES, ancDataBytes*8, BS_READER); + + errorStatus = pcmDmx_Parse ( + self, + hBs, + ancDataBytes*8, + isMpeg2 ); + + return (errorStatus); } +#endif /* DSE_METADATA_ENABLE */ +#ifdef PCE_METADATA_ENABLE /** Set the matrix mixdown information extracted from the PCE of an AAC bitstream. * Note: Call only if matrix_mixdown_idx_present is true. * @param [in] Handle of PCM downmix module instance. @@ -717,38 +1835,36 @@ PCMDMX_ERROR pcmDmx_SetMatrixMixdownFromPce ( int pseudoSurroundEnable ) { - MPEG_MIXDOWN_INFO *pMpegMixDownInfo; + DMX_BS_META_DATA *pBsMetaData = &self->bsMetaData[0]; if (self == NULL) { return (PCMDMX_INVALID_HANDLE); } - pMpegMixDownInfo = &self->mpegMixDownInfo[0]; - if (matrixMixdownPresent) { - pMpegMixDownInfo->matrixMixdownIdx = matrixMixdownIdx & 0x03; - pMpegMixDownInfo->pseudoSurroundEnable = pseudoSurroundEnable; + pBsMetaData->pseudoSurround = pseudoSurroundEnable; + pBsMetaData->matrixMixdownIdx = matrixMixdownIdx & 0x03; + pBsMetaData->typeFlags |= TYPE_PCE_DATA; + /* Reset expiry counter */ + pBsMetaData->expiryCount = 0; } - pMpegMixDownInfo->mixdownAvailable = matrixMixdownPresent; - /* Reset expiry counter */ - self->expiryCount = 0; - return (PCMDMX_OK); } +#endif /* PCE_METADATA_ENABLE */ /** Apply down or up mixing. * @param [in] Handle of PCM downmix module instance. - * @param [inout] Pointer to time buffer. Depending on interface configuration, the content of pTimeData is ignored, - * and the internal QMF buffer will be used as input data source. Otherwise, the MPEG Surround processing is - * applied to the timesignal pTimeData. For both variants, the resulting MPEG Surround signal is written into pTimeData. + * @param [inout] Pointer to buffer that hold the time domain signal. * @param [in] Pointer where the amount of output samples is returned into. * @param [inout] Pointer where the amount of output channels is returned into. * @param [in] Flag which indicates if output time data are writtern interleaved or as subsequent blocks. * @param [inout] Array where the corresponding channel type for each output audio channel is stored into. * @param [inout] Array where the corresponding channel type index for each output audio channel is stored into. - * @param [in] Array containing the output channel mapping to be used (From MPEG PCE ordering to whatever is required). + * @param [in] Array containing the out channel mapping to be used (From MPEG PCE ordering to whatever is required). + * @param [out] Pointer on a field receiving the scale factor that has to be applied on all samples afterwards. + * If the handed pointer is NULL scaling is done internally. * @returns Returns an error code. **/ PCMDMX_ERROR pcmDmx_ApplyFrame ( @@ -756,97 +1872,173 @@ PCMDMX_ERROR pcmDmx_ApplyFrame ( INT_PCM *pPcmBuf, UINT frameSize, INT *nChannels, - int fInterleaved, AUDIO_CHANNEL_TYPE channelType[], UCHAR channelIndices[], - const UCHAR channelMapping[][8] + const UCHAR channelMapping[][8], + INT *pDmxOutScale ) { + PCM_DMX_USER_PARAMS *pParam = NULL; PCMDMX_ERROR errorStatus = PCMDMX_OK; DUAL_CHANNEL_MODE dualChannelMode; PCM_DMX_CHANNEL_MODE inChMode; - int numOutChannels; - int numInChannels = *nChannels; - int slot; + PCM_DMX_CHANNEL_MODE outChMode; + INT devNull; /* Just a dummy to avoid a lot of branches in the code */ + int numOutChannels, numInChannels; + int inStride, outStride, offset; + int dmxMaxScale, dmxScale; + int ch, slot; UCHAR inOffsetTable[PCM_DMX_MAX_CHANNELS]; - MPEG_MIXDOWN_INFO mpegMixDownInfo; - DVB_MIXDOWN_LEVELS dvbMixDownLevels; + DMX_BS_META_DATA bsMetaData; - if (self == NULL) { return (PCMDMX_INVALID_HANDLE); } - - if ( (self->expiryFrame > 0) - && (++self->expiryCount > self->expiryFrame) ) - { /* The metadata read from bitstream is too old. */ - errorStatus = pcmDmx_Reset(self, PCMDMX_RESET_BS_DATA); + if ( (self == NULL) + || (nChannels == NULL) + || (channelType == NULL) + || (channelIndices == NULL) + || (channelMapping == NULL) ) { + return (PCMDMX_INVALID_HANDLE); } - FDKmemcpy(&mpegMixDownInfo, &self->mpegMixDownInfo[self->frameDelay], sizeof(MPEG_MIXDOWN_INFO)); - /* Maintain delay line */ - for (slot = self->frameDelay; slot > 0; slot -= 1) { - FDKmemcpy(&self->mpegMixDownInfo[slot], &self->mpegMixDownInfo[slot-1], sizeof(MPEG_MIXDOWN_INFO)); - } - FDKmemcpy(&dvbMixDownLevels, &self->dvbMixDownLevels[self->frameDelay], sizeof(DVB_MIXDOWN_LEVELS)); - /* Maintain delay line */ - for (slot = self->frameDelay; slot > 0; slot -= 1) { - FDKmemcpy(&self->dvbMixDownLevels[slot], &self->dvbMixDownLevels[slot-1], sizeof(DVB_MIXDOWN_LEVELS)); + /* Init the output scaling */ + dmxScale = 0; + if (pDmxOutScale != NULL) { + /* Avoid final scaling internally and hand it to the outside world. */ + *pDmxOutScale = 0; + dmxMaxScale = PCMDMX_MAX_HEADROOM; + } else { + /* Apply the scaling internally. */ + pDmxOutScale = &devNull; /* redirect to temporal stack memory */ + dmxMaxScale = 0; } - if (self->applyProcessing == 0) { return (errorStatus); } + pParam = &self->userParams; + numInChannels = *nChannels; + /* Perform some input sanity checks */ if (pPcmBuf == NULL) { return (PCMDMX_INVALID_ARGUMENT); } if (frameSize == 0) { return (PCMDMX_INVALID_ARGUMENT); } - if (numInChannels == 0) { return (PCMDMX_INVALID_ARGUMENT); } + if ( (numInChannels == 0) + || (numInChannels > PCM_DMX_MAX_IN_CHANNELS) ) + { return (PCMDMX_INVALID_ARGUMENT); } + + /* Check on misconfiguration */ + FDK_ASSERT( (pParam->numOutChannelsMax <= 0) \ + || (pParam->numOutChannelsMax >= pParam->numOutChannelsMin)); + + /* Determine if the module has to do processing */ + if ( (self->applyProcessing == 0) + && ((pParam->numOutChannelsMax <= 0) + || (pParam->numOutChannelsMax >= numInChannels)) + && (pParam->numOutChannelsMin <= numInChannels) ) { + /* Nothing to do */ + return (errorStatus); + } - if (self->numOutputChannels <= 0) { + /* Determine the number of output channels */ + if ( (pParam->numOutChannelsMax > 0) + && (numInChannels > pParam->numOutChannelsMax) ) { + numOutChannels = pParam->numOutChannelsMax; + } + else if (numInChannels < pParam->numOutChannelsMin) { + numOutChannels = pParam->numOutChannelsMin; + } + else { numOutChannels = numInChannels; - } else { - numOutChannels = self->numOutputChannels; } - dualChannelMode = self->dualChannelMode; + + dualChannelMode = pParam->dualChannelMode; /* Analyse input channel configuration and get channel offset * table that can be accessed with the fixed channel labels. */ - inChMode = getChannelMode( + errorStatus = getChannelMode( numInChannels, channelType, channelIndices, - inOffsetTable + inOffsetTable, + &inChMode ); - if (inChMode == CH_MODE_UNDEFINED) { + if ( PCMDMX_IS_FATAL_ERROR(errorStatus) + || (inChMode == CH_MODE_UNDEFINED) ) { /* We don't need to restore because the channel configuration has not been changed. Just exit. */ return (PCMDMX_INVALID_CH_CONFIG); } + /* Set input stride and offset */ + if (fInterleaved) { + inStride = numInChannels; + offset = 1; /* Channel specific offset factor */ + } else { + inStride = 1; + offset = frameSize; /* Channel specific offset factor */ + } + + /* Reset downmix meta data if necessary */ + if ( (pParam->expiryFrame > 0) + && (++self->bsMetaData[0].expiryCount > pParam->expiryFrame) ) + { /* The metadata read from bitstream is too old. */ + PCMDMX_ERROR err = pcmDmx_Reset(self, PCMDMX_RESET_BS_DATA); + FDK_ASSERT(err == PCMDMX_OK); + } + FDKmemcpy(&bsMetaData, &self->bsMetaData[pParam->frameDelay], sizeof(DMX_BS_META_DATA)); + /* Maintain delay line */ + for (slot = pParam->frameDelay; slot > 0; slot -= 1) { + FDKmemcpy(&self->bsMetaData[slot], &self->bsMetaData[slot-1], sizeof(DMX_BS_META_DATA)); + } + /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifdef PCM_DOWNMIX_ENABLE if ( numInChannels > numOutChannels ) { /* Apply downmix */ - INT_PCM *pInCF, *pInLF, *pInRF, *pInLO, *pInRO, *pInLR, *pInRR, *pOutL, *pOutR; - FIXP_SGL flev, clev, slev; - - UINT sample; - int inStride, outStride, offset; - int useGuidedDownMix = 0; - UCHAR outOffsetTable[PCM_DMX_MAX_CHANNELS]; - - /* Set I/O strides and offsets */ - if (fInterleaved) { - inStride = numInChannels; - outStride = TWO_CHANNEL; /* The output of STAGE ONE is always STEREO !!! - STAGE TWO creates a downmix to mono if required. */ - offset = 1; /* Channel specific offset factor */ - } else { - inStride = 1; - outStride = 1; - offset = frameSize; /* Channel specific offset factor */ + INT_PCM *pInPcm[PCM_DMX_MAX_IN_CHANNELS] = { NULL }; + INT_PCM *pOutPcm[PCM_DMX_MAX_OUT_CHANNELS] = { NULL }; + FIXP_DMX mixFactors[PCM_DMX_MAX_CHANNELS][PCM_DMX_MAX_CHANNELS]; + UCHAR outOffsetTable[PCM_DMX_MAX_CHANNELS]; + UINT sample; + int chCfg = 0; + int bypScale = 0; + +#if (PCM_DMX_MAX_IN_CHANNELS >= 7) + if (numInChannels > SIX_CHANNEL) { + AUDIO_CHANNEL_TYPE multiPurposeChType[2]; + + /* Get the type of the multipurpose channels */ + multiPurposeChType[0] = channelType[inOffsetTable[LEFT_MULTIPRPS_CHANNEL]]; + multiPurposeChType[1] = channelType[inOffsetTable[RIGHT_MULTIPRPS_CHANNEL]]; + + /* Check if the input configuration is one defined in the standard. */ + switch (inChMode) { + case CH_MODE_5_0_2_1: /* chCfg 7 || 14 */ + /* Further analyse the input config to distinguish the two CH_MODE_5_0_2_1 configs. */ + if ( (multiPurposeChType[0] == ACT_FRONT_TOP) + && (multiPurposeChType[1] == ACT_FRONT_TOP) ) { + chCfg = 14; + } else { + chCfg = 7; + } + break; + case CH_MODE_3_0_3_1: /* chCfg 11 */ + chCfg = 11; + break; + case CH_MODE_3_0_4_1: /* chCfg 12 */ + chCfg = 12; + break; + default: + chCfg = 0; /* Not a known config */ + break; + } } +#endif - /* Get channel description and channel mapping for this - * stages number of output channels (always STEREO). */ + /* Set this stages output stride and channel mode: */ + outStride = (fInterleaved) ? numOutChannels : 1; + outChMode = outChModeTable[numOutChannels]; + + /* Get channel description and channel mapping for the desired output configuration. */ getChannelDescription( - CH_MODE_2_0_0_0, + outChMode, channelMapping, channelType, channelIndices, @@ -854,287 +2046,300 @@ PCMDMX_ERROR pcmDmx_ApplyFrame ( ); /* Now there is no way back because we modified the channel configuration! */ - /* Set channel pointer for input */ - pInCF = &pPcmBuf[inOffsetTable[CENTER_FRONT_CHANNEL]*offset]; - pInLF = &pPcmBuf[inOffsetTable[LEFT_FRONT_CHANNEL]*offset]; - pInRF = &pPcmBuf[inOffsetTable[RIGHT_FRONT_CHANNEL]*offset]; - pInLO = &pPcmBuf[inOffsetTable[LEFT_OUTSIDE_CHANNEL]*offset]; - pInRO = &pPcmBuf[inOffsetTable[RIGHT_OUTSIDE_CHANNEL]*offset]; - pInLR = &pPcmBuf[inOffsetTable[LEFT_REAR_CHANNEL]*offset]; - pInRR = &pPcmBuf[inOffsetTable[RIGHT_REAR_CHANNEL]*offset]; - - /* Set channel pointer for output - Caution: Different channel mapping compared to input */ - pOutL = &pPcmBuf[outOffsetTable[LEFT_FRONT_CHANNEL]*offset]; /* LEFT_FRONT_CHANNEL */ - pOutR = &pPcmBuf[outOffsetTable[RIGHT_FRONT_CHANNEL]*offset]; /* RIGHT_FRONT_CHANNEL */ - - /* Set downmix levels: */ - flev = ATTENUATION_FACTOR_1; /* 0.707 */ - clev = ATTENUATION_FACTOR_1; /* 0.707 */ - slev = ATTENUATION_FACTOR_1; /* 0.707 */ - - if ( dvbMixDownLevels.mixLevelsAvail ) { - clev = dvbMixDownLevels.centerMixLevelValue; - slev = dvbMixDownLevels.surroundMixLevelValue; - useGuidedDownMix = 1; - } - - /* FIRST STAGE: - Always downmix to 2 channel output: */ - switch ( inChMode ) - { - case CH_MODE_2_0_0_0: - case CH_MODE_2_0_0_1: - /* 2/0 input: */ - switch (dualChannelMode) - { - case CH1_MODE: /* L' = 0.707 * Ch1; R' = 0.707 * Ch1 */ - for (sample = 0; sample < frameSize; sample++) { - *pOutL = *pOutR = - (INT_PCM)SATURATE_RIGHT_SHIFT(fMult((FIXP_PCM)*pInLF, flev), DFRACT_BITS-SAMPLE_BITS, SAMPLE_BITS); - - pInLF += inStride; - pOutL += outStride; pOutR += outStride; - } - break; - - case CH2_MODE: /* L' = 0.707 * Ch2; R' = 0.707 * Ch2 */ - for (sample = 0; sample < frameSize; sample++) { - *pOutL = *pOutR = - (INT_PCM)SATURATE_RIGHT_SHIFT(fMult((FIXP_PCM)*pInRF, flev), DFRACT_BITS-SAMPLE_BITS, SAMPLE_BITS); - - pInRF += inStride; - pOutL += outStride; pOutR += outStride; + /* Create the DMX matrix */ + errorStatus = getMixFactors ( + (chCfg>0) ? 1 : 0, + (chCfg>0) ? (PCM_DMX_CHANNEL_MODE)chCfg : inChMode, + outChMode, + pParam, + &bsMetaData, + mixFactors, + &dmxScale + ); + /* No fatal errors can occur here. The function is designed to always return a valid matrix. + The error code is used to signal configurations and matrices that are not conform to any standard. */ + + /* Determine the final scaling */ + bypScale = FDKmin(dmxMaxScale, dmxScale); + *pDmxOutScale += bypScale; + dmxScale -= bypScale; + + { /* Set channel pointer for input. Remove empty cols. */ + int inCh, outCh, map[PCM_DMX_MAX_CHANNELS]; + ch = 0; + for (inCh=0; inCh < PCM_DMX_MAX_CHANNELS; inCh+=1) { + if (inOffsetTable[inCh] != 255) { + pInPcm[ch] = &pPcmBuf[inOffsetTable[inCh]*offset]; + map[ch++] = inCh; } - break; - case MIXED_MODE: /* L' = 0.5*Ch1 + 0.5*Ch2; R' = 0.5*Ch1 + 0.5*Ch2 */ - for (sample = 0; sample < frameSize; sample++) { - *pOutL = *pOutR = (*pInLF >> 1) + (*pInRF >> 1); - - pInLF += inStride; pInRF += inStride; - pOutL += outStride; pOutR += outStride; - } - break; - default: - case STEREO_MODE: - /* nothing to do */ - break; } - break; - - case CH_MODE_3_0_0_0: - /* 3/0 input: L' = L + 0.707*C; R' = R + 0.707*C; */ - for (sample = 0; sample < frameSize; sample++) - { - FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev); -#if (SAMPLE_BITS == 32) - /* left channel */ - *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>1)+tCF, 1, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>1)+tCF, 1, SAMPLE_BITS); -#else - /* left channel */ - *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>1)+tCF, DFRACT_BITS-SAMPLE_BITS-1, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>1)+tCF, DFRACT_BITS-SAMPLE_BITS-1, SAMPLE_BITS); -#endif - pInLF += inStride; pInRF += inStride; pInCF += inStride; - pOutL += outStride; pOutR += outStride; + FDK_ASSERT(ch == numInChannels); + + /* Remove unused cols from factor matrix */ + for (inCh=0; inCh < numInChannels; inCh+=1) { + if (inCh != map[inCh]) { + int outCh; + for (outCh=0; outCh < PCM_DMX_MAX_CHANNELS; outCh+=1) { + mixFactors[outCh][inCh] = mixFactors[outCh][map[inCh]]; + } + } } - break; - /* 2/1 input: not supported! - case CH_MODE_2_0_1_0: */ - - case CH_MODE_3_0_1_0: - if (useGuidedDownMix) { - /* 3/1 input: L' = L + clev*C + 0.707*slev*S; R' = R + clev*C + 0.707*slev*S; */ - slev = FX_DBL2FX_SGL(fMult(flev, slev)); /* 0.707*slef */ - - for (sample = 0; sample < frameSize; sample++) - { - FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev) >> 1; - FIXP_DBL tLR = fMultDiv2((FIXP_PCM)*pInLR, slev) >> 1; -#if (SAMPLE_BITS == 32) - /* left channel */ - *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF+tLR, 2, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tLR, 2, SAMPLE_BITS); -#else - /* left channel */ - *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF-tLR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tLR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS); -#endif - pInLF += inStride; pInRF += inStride; pInCF += inStride; pInLR += inStride; - pOutL += outStride; pOutR += outStride; - } - } else { - /* 3/1 input: L' = L + 0.707*C - 0.707*S; R' = R + 0.707*C + 0.707*S */ - for (sample = 0; sample < frameSize; sample++) - { - FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev) >> 1; - FIXP_DBL tLR = fMultDiv2((FIXP_PCM)*pInLR, slev) >> 1; -#if (SAMPLE_BITS == 32) - /* left channel */ - *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF-tLR, 2, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tLR, 2, SAMPLE_BITS); -#else - /* left channel */ - *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF-tLR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tLR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS); -#endif - pInLF += inStride; pInRF += inStride; pInCF += inStride; pInLR += inStride; - pOutL += outStride; pOutR += outStride; + /* Set channel pointer for output. Remove empty cols. */ + ch = 0; + for (outCh=0; outCh < PCM_DMX_MAX_CHANNELS; outCh+=1) { + if (outOffsetTable[outCh] != 255) { + pOutPcm[ch] = &pPcmBuf[outOffsetTable[outCh]*offset]; + map[ch++] = outCh; } } - break; - - /* 2/2 input: not supported! - case CH_MODE_2_0_2_0: */ + FDK_ASSERT(ch == numOutChannels); - case CH_MODE_3_0_2_0: /* 5.0ch input */ - case CH_MODE_3_0_2_1: /* 5.1ch input */ - if (useGuidedDownMix) { - /* 3/2 input: L' = L + clev*C + slev*Ls; R' = R + clev*C + slev*Rs; */ - for (sample = 0; sample < frameSize; sample++) - { - FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev) >> 1; - FIXP_DBL tLR = fMultDiv2((FIXP_PCM)*pInLR, slev) >> 1; - FIXP_DBL tRR = fMultDiv2((FIXP_PCM)*pInRR, slev) >> 1; -#if (SAMPLE_BITS == 32) - /* left channel */ - *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF+tLR, 2, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tRR, 2, SAMPLE_BITS); -#else - /* left channel */ - *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF+tLR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tRR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS); -#endif - pInLF += inStride; pInRF += inStride; pInCF += inStride; pInLR += inStride; pInRR += inStride; - pOutL += outStride; pOutR += outStride; + /* Remove unused rows from factor matrix */ + for (outCh=0; outCh < numOutChannels; outCh+=1) { + if (outCh != map[outCh]) { + FDKmemcpy(&mixFactors[outCh], &mixFactors[map[outCh]], PCM_DMX_MAX_CHANNELS*sizeof(FIXP_DMX)); } } - else if (mpegMixDownInfo.mixdownAvailable) { - /* 3/2 input: L' = (1.707+A)^-1 * [L+0.707*C+A*Ls]; R'= (1.707+A)^-1 * [R+0.707*C+A*Rs]; */ - FIXP_SGL mtrxMixDwnCoef = mpegMixDownIdx2Coef[mpegMixDownInfo.matrixMixdownIdx]; - FIXP_SGL mtrxMixDwnPreFact = mpegMixDownIdx2PreFact[mpegMixDownInfo.matrixMixdownIdx]; - clev = FX_DBL2FX_SGL(fMult(mtrxMixDwnPreFact, flev /* 0.707 */)); - flev = mtrxMixDwnPreFact; - slev = FX_DBL2FX_SGL(fMult(mtrxMixDwnPreFact, mtrxMixDwnCoef)); - - for (sample = 0; sample < frameSize; sample++) - { - FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev); - FIXP_DBL tLF = fMultDiv2((FIXP_PCM)*pInLF, flev); - FIXP_DBL tRF = fMultDiv2((FIXP_PCM)*pInRF, flev); - FIXP_DBL tLR = fMultDiv2((FIXP_PCM)*pInLR, slev); - FIXP_DBL tRR = fMultDiv2((FIXP_PCM)*pInRR, slev); - -#if (SAMPLE_BITS == 32) - /* left channel */ - *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT(tLF+tCF+tLR, 1, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT(tRF+tCF+tRR, 1, SAMPLE_BITS); -#else - /* left channel */ - *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT(tLF+tCF+tLR, DFRACT_BITS-SAMPLE_BITS-1, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT(tRF+tCF+tRR, DFRACT_BITS-SAMPLE_BITS-1, SAMPLE_BITS); -#endif + } - pInLF += inStride; pInRF += inStride; pInCF += inStride; pInLR += inStride; pInRR += inStride; - pOutL += outStride; pOutR += outStride; - } + /* Sample processing loop */ + for (sample = 0; sample < frameSize; sample++) + { + FIXP_PCM tIn[PCM_DMX_MAX_IN_CHANNELS]; + FIXP_DBL tOut[PCM_DMX_MAX_OUT_CHANNELS] = { (FIXP_DBL)0 }; + int inCh, outCh; + + /* Preload all input samples */ + for (inCh=0; inCh < numInChannels; inCh+=1) { + tIn[inCh] = (FIXP_PCM)*pInPcm[inCh]; + pInPcm[inCh] += inStride; } - else { - /* 3/2 input: L' = L + 0.707*C - 0.707*Ls - 0.707*Rs; R' = R + 0.707*C + 0.707*Ls + 0.707*Rs */ - for (sample = 0; sample < frameSize; sample++) - { - FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev) >> 2; - FIXP_DBL tLR = fMultDiv2((FIXP_PCM)*pInLR, slev) >> 2; - FIXP_DBL tRR = fMultDiv2((FIXP_PCM)*pInRR, slev) >> 2; -#if (SAMPLE_BITS == 32) - /* left channel */ - *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>3)+tCF-tLR-tRR, 3, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>3)+tCF+tLR+tRR, 3, SAMPLE_BITS); + /* Apply downmix coefficients to input samples and accumulate for output */ + for (outCh=0; outCh < numOutChannels; outCh+=1) { + for (inCh=0; inCh < numInChannels; inCh+=1) { + tOut[outCh] += fMult(tIn[inCh], mixFactors[outCh][inCh]); + } + /* Write sample */ +#if (SAMPLE_BITS == DFRACT_BITS) + *pOutPcm[outCh] = (INT_PCM)SATURATE_LEFT_SHIFT(tOut[outCh], dmxScale, SAMPLE_BITS); #else - /* left channel */ - *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>3)+tCF-tLR-tRR, DFRACT_BITS-SAMPLE_BITS-3, SAMPLE_BITS); - /* right channel */ - *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>3)+tCF+tLR+tRR, DFRACT_BITS-SAMPLE_BITS-3, SAMPLE_BITS); + *pOutPcm[outCh] = (INT_PCM)SATURATE_RIGHT_SHIFT(tOut[outCh], DFRACT_BITS-SAMPLE_BITS-dmxScale, SAMPLE_BITS); #endif - pInLF += inStride; pInRF += inStride; pInCF += inStride; pInLR += inStride; pInRR += inStride; - pOutL += outStride; pOutR += outStride; - } + pOutPcm[outCh] += outStride; } - break; - - default: - errorStatus = PCMDMX_INVALID_MODE; - break; } - /* SECOND STAGE: - If desired create a mono donwmix: - Note: Input are always two channels! */ - if (numOutChannels == 1) - { - INT_PCM *pOutC; - FIXP_SGL mlev; - - if (useGuidedDownMix) mlev = FL2FXCONST_SGL(1.0f); else mlev = flev; + /* Update the number of output channels */ + *nChannels = numOutChannels; - /* Output of STAGE ONE = Input of STAGE TWO */ - FDKmemcpy(inOffsetTable, outOffsetTable, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR)); + } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + else +#endif /* PCM_DOWNMIX_ENABLE */ +#ifdef PCM_CHANNEL_EXTENSION_ENABLE + if ( numInChannels < numOutChannels ) + { /* Apply rudimentary upmix */ + /* Set up channel pointer */ + UINT sample; + UCHAR outOffsetTable[PCM_DMX_MAX_CHANNELS]; + + /* FIRST STAGE + Create a stereo/dual channel signal */ + if (numInChannels == ONE_CHANNEL) + { + INT_PCM *pInPcm[PCM_DMX_MAX_CHANNELS]; + INT_PCM *pOutLF, *pOutRF; - /* Set I/O strides and offsets */ - inStride = outStride; /* output from STAGE ONE */ - outStride = numOutChannels; /* final output */ + /* Set this stages output stride and channel mode: */ + outStride = (fInterleaved) ? TWO_CHANNEL : 1; + outChMode = outChModeTable[TWO_CHANNEL]; /* Get channel description and channel mapping for this - * stages number of output channels (always MONO). */ + * stages number of output channels (always STEREO). */ getChannelDescription( - CH_MODE_1_0_0_0, + outChMode, channelMapping, channelType, channelIndices, outOffsetTable ); + /* Now there is no way back because we modified the channel configuration! */ - /* Set input channel pointer. */ - pInLF = &pPcmBuf[inOffsetTable[LEFT_FRONT_CHANNEL]*offset]; - pInRF = &pPcmBuf[inOffsetTable[RIGHT_FRONT_CHANNEL]*offset]; + /* Set input channel pointer. The first channel is always at index 0. */ + pInPcm[CENTER_FRONT_CHANNEL] = &pPcmBuf[(frameSize-1)*inStride]; /* Considering input mapping could lead to a invalid pointer + here if the channel is not declared to be a front channel. */ - /* Set output channel pointer */ - pOutC = &pPcmBuf[outOffsetTable[CENTER_FRONT_CHANNEL]*offset]; + /* Set output channel pointer (for this stage). */ + pOutLF = &pPcmBuf[outOffsetTable[LEFT_FRONT_CHANNEL]*offset+(frameSize-1)*outStride]; + pOutRF = &pPcmBuf[outOffsetTable[RIGHT_FRONT_CHANNEL]*offset+(frameSize-1)*outStride]; - /* C' = 0.707*L + 0.707*R */ + /* 1/0 input: */ for (sample = 0; sample < frameSize; sample++) { -#if (SAMPLE_BITS == 32) - *pOutC = - (INT_PCM)SATURATE_LEFT_SHIFT(fMultDiv2((FIXP_PCM)*pInLF,mlev)+fMultDiv2((FIXP_PCM)*pInRF,mlev), 1, SAMPLE_BITS); -#else - *pOutC = - (INT_PCM)SATURATE_RIGHT_SHIFT(fMultDiv2((FIXP_PCM)*pInLF,mlev)+fMultDiv2((FIXP_PCM)*pInRF,mlev), DFRACT_BITS-SAMPLE_BITS-1, SAMPLE_BITS); -#endif + /* L' = C; R' = C; */ + *pOutLF = *pOutRF = *pInPcm[CENTER_FRONT_CHANNEL]; - pInLF += inStride; pInRF += inStride; - pOutC += 1; + pInPcm[CENTER_FRONT_CHANNEL] -= inStride; + pOutLF -= outStride; pOutRF -= outStride; } - /* Finished STAGE TWO */ + + /* Prepare for next stage: */ + inStride = outStride; + inChMode = outChMode; + FDKmemcpy(inOffsetTable, outOffsetTable, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR)); } - /* Update the number of output channels */ - *nChannels = self->numOutputChannels; +#if (PCM_DMX_MAX_OUT_CHANNELS > 2) + /* SECOND STAGE + Extend with zero channels to achieved the desired number of output channels. */ + if (numOutChannels > TWO_CHANNEL) + { + INT_PCM *pIn[PCM_DMX_MAX_CHANNELS] = { NULL }; + INT_PCM *pOut[PCM_DMX_MAX_CHANNELS] = { NULL }; + AUDIO_CHANNEL_TYPE inChTypes[PCM_DMX_MAX_CHANNELS]; + UCHAR inChIndices[PCM_DMX_MAX_CHANNELS]; + UCHAR numChPerGrp[2][PCM_DMX_MAX_CHANNEL_GROUPS]; + int nContentCh = 0; /* Number of channels with content */ + int nEmptyCh = 0; /* Number of channels with content */ + int ch, chGrp, isCompatible = 1; + + /* Do not change the signalling which is the channel types and indices. + Just reorder and add channels. So first save the input signalling. */ + FDKmemcpy(inChTypes, channelType, PCM_DMX_MAX_CHANNELS*sizeof(AUDIO_CHANNEL_TYPE)); + FDKmemcpy(inChIndices, channelIndices, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR)); + + /* Set this stages output stride and channel mode: */ + outStride = (fInterleaved) ? numOutChannels : 1; + outChMode = outChModeTable[numOutChannels]; + + /* Check if input channel config can be easily mapped to the desired output config. */ + for (chGrp = 0; chGrp < PCM_DMX_MAX_CHANNEL_GROUPS; chGrp += 1) { + numChPerGrp[IN][chGrp] = (inChMode >> (chGrp*4)) & 0xF; + numChPerGrp[OUT][chGrp] = (outChMode >> (chGrp*4)) & 0xF; + + if (numChPerGrp[IN][chGrp] > numChPerGrp[OUT][chGrp]) { + isCompatible = 0; + break; + } + } + if ( isCompatible ) { + /* Get new channel description and channel + * mapping for the desired output channel mode. */ + getChannelDescription( + outChMode, + channelMapping, + channelType, + channelIndices, + outOffsetTable + ); + /* If the input config has a back center channel but the output + config has not, copy it to left and right (if available). */ + if ( (numChPerGrp[IN][CH_GROUP_REAR]%2) + && !(numChPerGrp[OUT][CH_GROUP_REAR]%2) ) { + if (numChPerGrp[IN][CH_GROUP_REAR] == 1) { + inOffsetTable[RIGHT_REAR_CHANNEL] = inOffsetTable[LEFT_REAR_CHANNEL]; + } else if (numChPerGrp[IN][CH_GROUP_REAR] == 3) { + inOffsetTable[RIGHT_MULTIPRPS_CHANNEL] = inOffsetTable[LEFT_MULTIPRPS_CHANNEL]; + } + } + } + else { + /* Just copy and extend the original config */ + FDKmemcpy(outOffsetTable, inOffsetTable, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR)); + } + + /* Set I/O channel pointer. + Note: The following assignment algorithm clears the channel offset tables. + Thus they can not be used afterwards. */ + for (ch = 0; ch < PCM_DMX_MAX_CHANNELS; ch+=1) { + if ( (outOffsetTable[ch] < 255) + && (inOffsetTable[ch] < 255) ) + { /* Set I/O pointer: */ + pIn[nContentCh] = &pPcmBuf[inOffsetTable[ch]*offset+(frameSize-1)*inStride]; + pOut[nContentCh] = &pPcmBuf[outOffsetTable[ch]*offset+(frameSize-1)*outStride]; + /* Update signalling */ + channelType[outOffsetTable[ch]] = inChTypes[inOffsetTable[ch]]; + channelIndices[outOffsetTable[ch]] = inChIndices[inOffsetTable[ch]]; + inOffsetTable[ch] = 255; + outOffsetTable[ch] = 255; + nContentCh += 1; + } + } + if ( isCompatible ) { + /* Assign the remaining input channels. + This is just a safety appliance. We should never need it. */ + for (ch = 0; ch < PCM_DMX_MAX_CHANNELS; ch+=1) { + if (inOffsetTable[ch] < 255) { + int outCh; + for (outCh = 0 ; outCh < PCM_DMX_MAX_CHANNELS; outCh += 1) { + if (outOffsetTable[outCh] < 255) { + break; + } + } + /* Set I/O pointer: */ + pIn[nContentCh] = &pPcmBuf[inOffsetTable[ch]*offset+(frameSize-1)*inStride]; + pOut[nContentCh] = &pPcmBuf[outOffsetTable[outCh]*offset+(frameSize-1)*outStride]; + /* Update signalling */ + channelType[outOffsetTable[outCh]] = inChTypes[inOffsetTable[ch]]; + channelIndices[outOffsetTable[outCh]] = inChIndices[inOffsetTable[ch]]; + inOffsetTable[ch] = 255; + outOffsetTable[outCh] = 255; + nContentCh += 1; + } + } + /* Set the remaining output channel pointer */ + for (ch = 0; ch < PCM_DMX_MAX_CHANNELS; ch+=1) { + if (outOffsetTable[ch] < 255) { + pOut[nContentCh+nEmptyCh] = &pPcmBuf[outOffsetTable[ch]*offset+(frameSize-1)*outStride]; + /* Expand output signalling */ + channelType[outOffsetTable[ch]] = ACT_NONE; + channelIndices[outOffsetTable[ch]] = nEmptyCh; + outOffsetTable[ch] = 255; + nEmptyCh += 1; + } + } + } + else { + /* Set the remaining output channel pointer */ + for (ch = nContentCh; ch < numOutChannels; ch+=1) { + pOut[ch] = &pPcmBuf[ch*offset+(frameSize-1)*outStride]; + /* Expand output signalling */ + channelType[ch] = ACT_NONE; + channelIndices[ch] = nEmptyCh; + nEmptyCh += 1; + } + } + + /* First copy the channels that have signal */ + for (sample = 0; sample < frameSize; sample+=1) { + INT_PCM tIn[PCM_DMX_MAX_CHANNELS]; + /* Read all channel samples */ + for (ch = 0; ch < nContentCh; ch+=1) { + tIn[ch] = *pIn[ch]; + pIn[ch] -= inStride; + } + /* Write all channel samples */ + for (ch = 0; ch < nContentCh; ch+=1) { + *pOut[ch] = tIn[ch]; + pOut[ch] -= outStride; + } + } + + /* Clear all the other channels */ + for (sample = 0; sample < frameSize; sample++) { + for (ch = nContentCh; ch < numOutChannels; ch+=1) { + *pOut[ch] = (INT_PCM)0; + pOut[ch] -= outStride; + } + } + } +#endif /* if (PCM_DMX_MAX_OUT_CHANNELS > 2) */ + + /* update the number of output channels */ + *nChannels = numOutChannels; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ else +#endif /* PCM_CHANNEL_EXTENSION_ENABLE */ if ( numInChannels == numOutChannels ) { /* Don't need to change the channel description here */ @@ -1143,8 +2348,9 @@ PCMDMX_ERROR pcmDmx_ApplyFrame ( { case 2: { /* Set up channel pointer */ - INT_PCM *pInLF, *pInRF, *pOutL, *pOutR; - FIXP_SGL flev; + INT_PCM *pInPcm[PCM_DMX_MAX_CHANNELS]; + INT_PCM *pOutL, *pOutR; + FIXP_DMX flev; UINT sample; int inStride, outStride, offset; @@ -1160,41 +2366,41 @@ PCMDMX_ERROR pcmDmx_ApplyFrame ( } /* Set input channel pointer */ - pInLF = &pPcmBuf[inOffsetTable[LEFT_FRONT_CHANNEL]*offset]; - pInRF = &pPcmBuf[inOffsetTable[RIGHT_FRONT_CHANNEL]*offset]; + pInPcm[LEFT_FRONT_CHANNEL] = &pPcmBuf[inOffsetTable[LEFT_FRONT_CHANNEL]*offset]; + pInPcm[RIGHT_FRONT_CHANNEL] = &pPcmBuf[inOffsetTable[RIGHT_FRONT_CHANNEL]*offset]; /* Set output channel pointer (same as input) */ - pOutL = pInLF; - pOutR = pInRF; + pOutL = pInPcm[LEFT_FRONT_CHANNEL]; + pOutR = pInPcm[RIGHT_FRONT_CHANNEL]; /* Set downmix levels: */ - flev = ATTENUATION_FACTOR_1; /* 0.707 */ + flev = FL2FXCONST_DMX(0.70710678f); /* 2/0 input: */ switch (dualChannelMode) { case CH1_MODE: /* L' = 0.707 * Ch1; R' = 0.707 * Ch1 */ for (sample = 0; sample < frameSize; sample++) { *pOutL = *pOutR = - (INT_PCM)SATURATE_RIGHT_SHIFT(fMult((FIXP_PCM)*pInLF, flev), DFRACT_BITS-SAMPLE_BITS, SAMPLE_BITS); + (INT_PCM)SATURATE_RIGHT_SHIFT(fMult((FIXP_PCM)*pInPcm[LEFT_FRONT_CHANNEL], flev), DFRACT_BITS-SAMPLE_BITS, SAMPLE_BITS); - pInLF += inStride; + pInPcm[LEFT_FRONT_CHANNEL] += inStride; pOutL += outStride; pOutR += outStride; } break; case CH2_MODE: /* L' = 0.707 * Ch2; R' = 0.707 * Ch2 */ for (sample = 0; sample < frameSize; sample++) { *pOutL = *pOutR = - (INT_PCM)SATURATE_RIGHT_SHIFT(fMult((FIXP_PCM)*pInRF, flev), DFRACT_BITS-SAMPLE_BITS, SAMPLE_BITS); + (INT_PCM)SATURATE_RIGHT_SHIFT(fMult((FIXP_PCM)*pInPcm[RIGHT_FRONT_CHANNEL], flev), DFRACT_BITS-SAMPLE_BITS, SAMPLE_BITS); - pInRF += inStride; + pInPcm[RIGHT_FRONT_CHANNEL] += inStride; pOutL += outStride; pOutR += outStride; } break; case MIXED_MODE: /* L' = 0.5*Ch1 + 0.5*Ch2; R' = 0.5*Ch1 + 0.5*Ch2 */ for (sample = 0; sample < frameSize; sample++) { - *pOutL = *pOutR = (*pInLF >> 1) + (*pInRF >> 1); + *pOutL = *pOutR = (*pInPcm[LEFT_FRONT_CHANNEL] >> 1) + (*pInPcm[RIGHT_FRONT_CHANNEL] >> 1); - pInLF += inStride; pInRF += inStride; + pInPcm[LEFT_FRONT_CHANNEL] += inStride; pInPcm[RIGHT_FRONT_CHANNEL] += inStride; pOutL += outStride; pOutR += outStride; } break; @@ -1254,23 +2460,37 @@ PCMDMX_ERROR pcmDmx_GetLibInfo( LIB_INFO *info ) if (i == FDK_MODULE_LAST) { return PCMDMX_UNKNOWN; } - info += i; /* Add the library info */ - info->module_id = FDK_PCMDMX; - info->version = LIB_VERSION(PCMDMX_LIB_VL0, PCMDMX_LIB_VL1, PCMDMX_LIB_VL2); - LIB_VERSION_STRING(info); - info->build_date = PCMDMX_LIB_BUILD_DATE; - info->build_time = PCMDMX_LIB_BUILD_TIME; - info->title = PCMDMX_LIB_TITLE; + info[i].module_id = FDK_PCMDMX; + info[i].version = LIB_VERSION(PCMDMX_LIB_VL0, PCMDMX_LIB_VL1, PCMDMX_LIB_VL2); + LIB_VERSION_STRING(info+i); + info[i].build_date = PCMDMX_LIB_BUILD_DATE; + info[i].build_time = PCMDMX_LIB_BUILD_TIME; + info[i].title = PCMDMX_LIB_TITLE; /* Set flags */ - info->flags = 0 + info[i].flags = 0 +#ifdef PCM_DOWNMIX_ENABLE | CAPF_DMX_BLIND /* At least blind downmixing is possible */ + #ifdef PCE_METADATA_ENABLE | CAPF_DMX_PCE /* Guided downmix with data from MPEG-2/4 Program Config Elements (PCE). */ + #ifdef ARIB_MIXDOWN_ENABLE + | CAPF_DMX_ARIB /* PCE guided downmix with slightly different equations and levels. */ + #endif + #endif /* PCE_METADATA_ENABLE */ + #ifdef DSE_METADATA_ENABLE | CAPF_DMX_DVB /* Guided downmix with data from DVB ancillary data fields. */ + #endif +#endif /* PCM_DOWNMIX_ENABLE */ +#ifdef PCM_CHANNEL_EXTENSION_ENABLE + | CAPF_DMX_CH_EXP /* Simple upmixing by dublicating channels or adding zero channels. */ +#endif ; + /* Add lib info for FDK tools (if not yet done). */ + FDK_toolsGetLibInfo(info); + return PCMDMX_OK; } |