Home | History | Annotate | Download | only in src
      1 /* -----------------------------------------------------------------------------
      2 Software License for The Fraunhofer FDK AAC Codec Library for Android
      3 
      4  Copyright  1995 - 2018 Fraunhofer-Gesellschaft zur Frderung der angewandten
      5 Forschung e.V. All rights reserved.
      6 
      7  1.    INTRODUCTION
      8 The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software
      9 that implements the MPEG Advanced Audio Coding ("AAC") encoding and decoding
     10 scheme for digital audio. This FDK AAC Codec software is intended to be used on
     11 a wide variety of Android devices.
     12 
     13 AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient
     14 general perceptual audio codecs. AAC-ELD is considered the best-performing
     15 full-bandwidth communications codec by independent studies and is widely
     16 deployed. AAC has been standardized by ISO and IEC as part of the MPEG
     17 specifications.
     18 
     19 Patent licenses for necessary patent claims for the FDK AAC Codec (including
     20 those of Fraunhofer) may be obtained through Via Licensing
     21 (www.vialicensing.com) or through the respective patent owners individually for
     22 the purpose of encoding or decoding bit streams in products that are compliant
     23 with the ISO/IEC MPEG audio standards. Please note that most manufacturers of
     24 Android devices already license these patent claims through Via Licensing or
     25 directly from the patent owners, and therefore FDK AAC Codec software may
     26 already be covered under those patent licenses when it is used for those
     27 licensed purposes only.
     28 
     29 Commercially-licensed AAC software libraries, including floating-point versions
     30 with enhanced sound quality, are also available from Fraunhofer. Users are
     31 encouraged to check the Fraunhofer website for additional applications
     32 information and documentation.
     33 
     34 2.    COPYRIGHT LICENSE
     35 
     36 Redistribution and use in source and binary forms, with or without modification,
     37 are permitted without payment of copyright license fees provided that you
     38 satisfy the following conditions:
     39 
     40 You must retain the complete text of this software license in redistributions of
     41 the FDK AAC Codec or your modifications thereto in source code form.
     42 
     43 You must retain the complete text of this software license in the documentation
     44 and/or other materials provided with redistributions of the FDK AAC Codec or
     45 your modifications thereto in binary form. You must make available free of
     46 charge copies of the complete source code of the FDK AAC Codec and your
     47 modifications thereto to recipients of copies in binary form.
     48 
     49 The name of Fraunhofer may not be used to endorse or promote products derived
     50 from this library without prior written permission.
     51 
     52 You may not charge copyright license fees for anyone to use, copy or distribute
     53 the FDK AAC Codec software or your modifications thereto.
     54 
     55 Your modified versions of the FDK AAC Codec must carry prominent notices stating
     56 that you changed the software and the date of any change. For modified versions
     57 of the FDK AAC Codec, the term "Fraunhofer FDK AAC Codec Library for Android"
     58 must be replaced by the term "Third-Party Modified Version of the Fraunhofer FDK
     59 AAC Codec Library for Android."
     60 
     61 3.    NO PATENT LICENSE
     62 
     63 NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without
     64 limitation the patents of Fraunhofer, ARE GRANTED BY THIS SOFTWARE LICENSE.
     65 Fraunhofer provides no warranty of patent non-infringement with respect to this
     66 software.
     67 
     68 You may use this FDK AAC Codec software or modifications thereto only for
     69 purposes that are authorized by appropriate patent licenses.
     70 
     71 4.    DISCLAIMER
     72 
     73 This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright
     74 holders and contributors "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
     75 including but not limited to the implied warranties of merchantability and
     76 fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
     77 CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary,
     78 or consequential damages, including but not limited to procurement of substitute
     79 goods or services; loss of use, data, or profits, or business interruption,
     80 however caused and on any theory of liability, whether in contract, strict
     81 liability, or tort (including negligence), arising in any way out of the use of
     82 this software, even if advised of the possibility of such damage.
     83 
     84 5.    CONTACT INFORMATION
     85 
     86 Fraunhofer Institute for Integrated Circuits IIS
     87 Attention: Audio and Multimedia Departments - FDK AAC LL
     88 Am Wolfsmantel 33
     89 91058 Erlangen, Germany
     90 
     91 www.iis.fraunhofer.de/amm
     92 amm-info (at) iis.fraunhofer.de
     93 ----------------------------------------------------------------------------- */
     94 
     95 /**************************** PCM utility library ******************************
     96 
     97    Author(s):   Christian Griebel
     98 
     99    Description: Defines functions that perform downmixing or a simple channel
    100                 expansion in the PCM time domain.
    101 
    102 *******************************************************************************/
    103 
    104 #include "pcmdmx_lib.h"
    105 
    106 #include "genericStds.h"
    107 #include "fixpoint_math.h"
    108 #include "FDK_core.h"
    109 
    110 /* library version */
    111 #include "version.h"
    112 /* library title */
    113 #define PCMDMX_LIB_TITLE "PCM Downmix Lib"
    114 
    115 #define FALSE 0
    116 #define TRUE 1
    117 #define IN 0
    118 #define OUT 1
    119 
    120 /* Type definitions: */
    121 #define FIXP_DMX FIXP_SGL
    122 #define FX_DMX2FX_DBL(x) FX_SGL2FX_DBL((FIXP_SGL)(x))
    123 #define FX_DBL2FX_DMX(x) FX_DBL2FX_SGL(x)
    124 #define FL2FXCONST_DMX(x) FL2FXCONST_SGL(x)
    125 #define MAXVAL_DMX MAXVAL_SGL
    126 #define FX_DMX2SHRT(x) ((SHORT)(x))
    127 #define FX_DMX2FL(x) FX_DBL2FL(FX_DMX2FX_DBL(x))
    128 
    129 /* Fixed and unique channel group indices.
    130  * The last group index has to be smaller than ( 4 ). */
    131 #define CH_GROUP_FRONT (0)
    132 #define CH_GROUP_SIDE (1)
    133 #define CH_GROUP_REAR (2)
    134 #define CH_GROUP_LFE (3)
    135 
    136 /* Fixed and unique channel plain indices. */
    137 #define CH_PLAIN_NORMAL (0)
    138 #define CH_PLAIN_TOP (1)
    139 #define CH_PLAIN_BOTTOM (2)
    140 
    141 /* The ordering of the following fixed channel labels has to be in MPEG-4 style.
    142  * From the center to the back with left and right channel interleaved (starting
    143  * with left). The last channel label index has to be smaller than ( 8 ). */
    144 #define CENTER_FRONT_CHANNEL (0) /* C  */
    145 #define LEFT_FRONT_CHANNEL (1)   /* L  */
    146 #define RIGHT_FRONT_CHANNEL (2)  /* R  */
    147 #define LEFT_REAR_CHANNEL \
    148   (3) /* Lr (aka left back channel) or center back channel */
    149 #define RIGHT_REAR_CHANNEL (4)      /* Rr (aka right back channel) */
    150 #define LOW_FREQUENCY_CHANNEL (5)   /* Lf */
    151 #define LEFT_MULTIPRPS_CHANNEL (6)  /* Left multipurpose channel */
    152 #define RIGHT_MULTIPRPS_CHANNEL (7) /* Right multipurpose channel */
    153 
    154 /* 22.2 channel specific fixed channel lables: */
    155 #define LEFT_SIDE_CHANNEL (8)            /* Lss  */
    156 #define RIGHT_SIDE_CHANNEL (9)           /* Rss  */
    157 #define CENTER_REAR_CHANNEL (10)         /* Cs   */
    158 #define CENTER_FRONT_CHANNEL_TOP (11)    /* Cv   */
    159 #define LEFT_FRONT_CHANNEL_TOP (12)      /* Lv   */
    160 #define RIGHT_FRONT_CHANNEL_TOP (13)     /* Rv   */
    161 #define LEFT_SIDE_CHANNEL_TOP (14)       /* Lvss */
    162 #define RIGHT_SIDE_CHANNEL_TOP (15)      /* Rvss */
    163 #define CENTER_SIDE_CHANNEL_TOP (16)     /* Ts   */
    164 #define LEFT_REAR_CHANNEL_TOP (17)       /* Lvr  */
    165 #define RIGHT_REAR_CHANNEL_TOP (18)      /* Rvr  */
    166 #define CENTER_REAR_CHANNEL_TOP (19)     /* Cvr  */
    167 #define CENTER_FRONT_CHANNEL_BOTTOM (20) /* Cb   */
    168 #define LEFT_FRONT_CHANNEL_BOTTOM (21)   /* Lb   */
    169 #define RIGHT_FRONT_CHANNEL_BOTTOM (22)  /* Rb   */
    170 #define LOW_FREQUENCY_CHANNEL_2 (23)     /* LFE2 */
    171 
    172 /* More constants */
    173 #define ONE_CHANNEL (1)
    174 #define TWO_CHANNEL (2)
    175 #define SIX_CHANNEL (6)
    176 #define EIGHT_CHANNEL (8)
    177 #define TWENTY_FOUR_CHANNEL (24)
    178 
    179 #define PCMDMX_THRESHOLD_MAP_HEAT_1 (0) /* Store only exact matches */
    180 #define PCMDMX_THRESHOLD_MAP_HEAT_2 (20)
    181 #define PCMDMX_THRESHOLD_MAP_HEAT_3 \
    182   (256) /* Do not assign normal channels to LFE */
    183 
    184 #define SP_Z_NRM (0)
    185 #define SP_Z_TOP (2)
    186 #define SP_Z_BOT (-2)
    187 #define SP_Z_LFE (-18)
    188 #define SP_Z_MUL (8) /* Should be smaller than SP_Z_LFE */
    189 
    190 typedef struct {
    191   SCHAR x; /* horizontal position:  center (0), left (-), right (+) */
    192   SCHAR y; /* deepth position:      front, side, back, position */
    193   SCHAR z; /* heigth positions:     normal, top, bottom, lfe */
    194 } PCM_DMX_SPEAKER_POSITION;
    195 
    196 /* CAUTION: The maximum x-value should be less or equal to
    197  * PCMDMX_SPKR_POS_X_MAX_WIDTH. */
    198 static const PCM_DMX_SPEAKER_POSITION spkrSlotPos[] = {
    199     /*  x,  y,  z  */
    200     {0, 0, SP_Z_NRM},  /* 0  CENTER_FRONT_CHANNEL        */
    201     {-2, 0, SP_Z_NRM}, /* 1  LEFT_FRONT_CHANNEL          */
    202     {2, 0, SP_Z_NRM},  /* 2  RIGHT_FRONT_CHANNEL         */
    203     {-3, 4, SP_Z_NRM}, /* 3  LEFT_REAR_CHANNEL           */
    204     {3, 4, SP_Z_NRM},  /* 4  RIGHT_REAR_CHANNEL          */
    205     {0, 0, SP_Z_LFE},  /* 5  LOW_FREQUENCY_CHANNEL       */
    206     {-2, 2, SP_Z_MUL}, /* 6  LEFT_MULTIPRPS_CHANNEL      */
    207     {2, 2, SP_Z_MUL}   /* 7  RIGHT_MULTIPRPS_CHANNEL     */
    208 };
    209 
    210 /* List of packed channel modes */
    211 typedef enum { /* CH_MODE_<numFrontCh>_<numSideCh>_<numBackCh>_<numLfCh> */
    212                CH_MODE_UNDEFINED = 0x0000,
    213                /* 1 channel */
    214                CH_MODE_1_0_0_0 = 0x0001, /* chCfg 1 */
    215                /* 2 channels */
    216                CH_MODE_2_0_0_0 = 0x0002 /* chCfg 2 */
    217                                         /* 3 channels */
    218                ,
    219                CH_MODE_3_0_0_0 = 0x0003, /* chCfg 3 */
    220                CH_MODE_2_0_1_0 = 0x0102,
    221                CH_MODE_2_0_0_1 = 0x1002,
    222                /* 4 channels */
    223                CH_MODE_3_0_1_0 = 0x0103, /* chCfg 4 */
    224                CH_MODE_2_0_2_0 = 0x0202,
    225                CH_MODE_2_0_1_1 = 0x1102,
    226                CH_MODE_4_0_0_0 = 0x0004,
    227                /* 5 channels */
    228                CH_MODE_3_0_2_0 = 0x0203, /* chCfg 5 */
    229                CH_MODE_2_0_2_1 = 0x1202,
    230                CH_MODE_3_0_1_1 = 0x1103,
    231                CH_MODE_3_2_0_0 = 0x0023,
    232                CH_MODE_5_0_0_0 = 0x0005,
    233                /* 6 channels */
    234                CH_MODE_3_0_2_1 = 0x1203, /* chCfg 6 */
    235                CH_MODE_3_2_0_1 = 0x1023,
    236                CH_MODE_3_2_1_0 = 0x0123,
    237                CH_MODE_5_0_1_0 = 0x0105,
    238                CH_MODE_6_0_0_0 = 0x0006,
    239                /* 7 channels */
    240                CH_MODE_2_2_2_1 = 0x1222,
    241                CH_MODE_3_0_3_1 = 0x1303, /* chCfg 11 */
    242                CH_MODE_3_2_1_1 = 0x1123,
    243                CH_MODE_3_2_2_0 = 0x0223,
    244                CH_MODE_3_0_2_2 = 0x2203,
    245                CH_MODE_5_0_2_0 = 0x0205,
    246                CH_MODE_5_0_1_1 = 0x1105,
    247                CH_MODE_7_0_0_0 = 0x0007,
    248                /* 8 channels */
    249                CH_MODE_3_2_2_1 = 0x1223,
    250                CH_MODE_3_0_4_1 = 0x1403, /* chCfg 12 */
    251                CH_MODE_5_0_2_1 = 0x1205, /* chCfg 7 + 14 */
    252                CH_MODE_5_2_1_0 = 0x0125,
    253                CH_MODE_3_2_1_2 = 0x2123,
    254                CH_MODE_2_2_2_2 = 0x2222,
    255                CH_MODE_3_0_3_2 = 0x2303,
    256                CH_MODE_8_0_0_0 = 0x0008
    257 
    258 } PCM_DMX_CHANNEL_MODE;
    259 
    260 /* These are the channel configurations linked to
    261    the number of output channels give by the user: */
    262 static const PCM_DMX_CHANNEL_MODE outChModeTable[(8) + 1] = {
    263     CH_MODE_UNDEFINED,
    264     CH_MODE_1_0_0_0, /* 1 channel  */
    265     CH_MODE_2_0_0_0  /* 2 channels */
    266     ,
    267     CH_MODE_3_0_0_0, /* 3 channels */
    268     CH_MODE_3_0_1_0, /* 4 channels */
    269     CH_MODE_3_0_2_0, /* 5 channels */
    270     CH_MODE_3_0_2_1  /* 6 channels */
    271     ,
    272     CH_MODE_3_0_3_1, /* 7 channels */
    273     CH_MODE_3_0_4_1  /* 8 channels */
    274 };
    275 
    276 static const FIXP_DMX abMixLvlValueTab[8] = {
    277     FL2FXCONST_DMX(0.500f), /* scaled by 1 */
    278     FL2FXCONST_DMX(0.841f), FL2FXCONST_DMX(0.707f), FL2FXCONST_DMX(0.596f),
    279     FL2FXCONST_DMX(0.500f), FL2FXCONST_DMX(0.422f), FL2FXCONST_DMX(0.355f),
    280     FL2FXCONST_DMX(0.0f)};
    281 
    282 static const FIXP_DMX lfeMixLvlValueTab[16] = {
    283     /*             value,        scale */
    284     FL2FXCONST_DMX(0.7905f), /*     2 */
    285     FL2FXCONST_DMX(0.5000f), /*     2 */
    286     FL2FXCONST_DMX(0.8395f), /*     1 */
    287     FL2FXCONST_DMX(0.7065f), /*     1 */
    288     FL2FXCONST_DMX(0.5945f), /*     1 */
    289     FL2FXCONST_DMX(0.500f),  /*     1 */
    290     FL2FXCONST_DMX(0.841f),  /*     0 */
    291     FL2FXCONST_DMX(0.707f),  /*     0 */
    292     FL2FXCONST_DMX(0.596f),  /*     0 */
    293     FL2FXCONST_DMX(0.500f),  /*     0 */
    294     FL2FXCONST_DMX(0.316f),  /*     0 */
    295     FL2FXCONST_DMX(0.178f),  /*     0 */
    296     FL2FXCONST_DMX(0.100f),  /*     0 */
    297     FL2FXCONST_DMX(0.032f),  /*     0 */
    298     FL2FXCONST_DMX(0.010f),  /*     0 */
    299     FL2FXCONST_DMX(0.000f)   /*     0 */
    300 };
    301 
    302 /* MPEG matrix mixdown:
    303     Set 1:  L' = (1 + 2^-0.5 + A )^-1 * [L + C * 2^-0.5 + A * Ls];
    304             R' = (1 + 2^-0.5 + A )^-1 * [R + C * 2^-0.5 + A * Rs];
    305 
    306     Set 2:  L' = (1 + 2^-0.5 + 2A )^-1 * [L + C * 2^-0.5 - A * (Ls + Rs)];
    307             R' = (1 + 2^-0.5 + 2A )^-1 * [R + C * 2^-0.5 + A * (Ls + Rs)];
    308 
    309     M = (3 + 2A)^-1 * [L + C + R + A*(Ls + Rs)];
    310 */
    311 static const FIXP_DMX mpegMixDownIdx2Coef[4] = {
    312     FL2FXCONST_DMX(0.70710678f), FL2FXCONST_DMX(0.5f),
    313     FL2FXCONST_DMX(0.35355339f), FL2FXCONST_DMX(0.0f)};
    314 
    315 static const FIXP_DMX mpegMixDownIdx2PreFact[3][4] = {
    316     {/* Set 1: */
    317      FL2FXCONST_DMX(0.4142135623730950f), FL2FXCONST_DMX(0.4530818393219728f),
    318      FL2FXCONST_DMX(0.4852813742385703f), FL2FXCONST_DMX(0.5857864376269050f)},
    319     {/* Set 2: */
    320      FL2FXCONST_DMX(0.3203772410170407f), FL2FXCONST_DMX(0.3693980625181293f),
    321      FL2FXCONST_DMX(0.4142135623730950f), FL2FXCONST_DMX(0.5857864376269050f)},
    322     {/* Mono DMX set: */
    323      FL2FXCONST_DMX(0.2265409196609864f), FL2FXCONST_DMX(0.25f),
    324      FL2FXCONST_DMX(0.2697521433898179f), FL2FXCONST_DMX(0.3333333333333333f)}};
    325 
    326 #define TYPE_NONE (0x00)
    327 #define TYPE_PCE_DATA (0x01)
    328 #define TYPE_DSE_CLEV_DATA (0x02)
    329 #define TYPE_DSE_SLEV_DATA (0x04)
    330 #define TYPE_DSE_DMIX_AB_DATA (0x08)
    331 #define TYPE_DSE_DMIX_LFE_DATA (0x10)
    332 #define TYPE_DSE_DMX_GAIN_DATA (0x20)
    333 #define TYPE_DSE_DMX_CGL_DATA (0x40)
    334 #define TYPE_DSE_DATA (0x7E)
    335 
    336 typedef struct {
    337   UINT typeFlags;
    338   /* From DSE */
    339   UCHAR cLevIdx;
    340   UCHAR sLevIdx;
    341   UCHAR dmixIdxA;
    342   UCHAR dmixIdxB;
    343   UCHAR dmixIdxLfe;
    344   UCHAR dmxGainIdx2;
    345   UCHAR dmxGainIdx5;
    346   /* From PCE */
    347   UCHAR matrixMixdownIdx;
    348   /* Attributes: */
    349   SCHAR pseudoSurround; /*!< If set to 1 the signal is pseudo surround
    350                            compatible. The value 0 tells that it is not. If the
    351                            value is -1 the information is not available.  */
    352   UINT expiryCount; /*!< Counter to monitor the life time of a meta data set. */
    353 
    354 } DMX_BS_META_DATA;
    355 
    356 /* Default metadata */
    357 static const DMX_BS_META_DATA dfltMetaData = {0, 2, 2, 2,  2, 15,
    358                                               0, 0, 0, -1, 0};
    359 
    360 /* Dynamic (user) params:
    361      See the definition of PCMDMX_PARAM for details on the specific fields. */
    362 typedef struct {
    363   DMX_PROFILE_TYPE dmxProfile; /*!< Linked to DMX_PRFL_STANDARD              */
    364   UINT expiryFrame;            /*!< Linked to DMX_BS_DATA_EXPIRY_FRAME       */
    365   DUAL_CHANNEL_MODE dualChannelMode; /*!< Linked to DMX_DUAL_CHANNEL_MODE */
    366   PSEUDO_SURROUND_MODE
    367   pseudoSurrMode;          /*!< Linked to DMX_PSEUDO_SURROUND_MODE       */
    368   SHORT numOutChannelsMin; /*!< Linked to MIN_NUMBER_OF_OUTPUT_CHANNELS  */
    369   SHORT numOutChannelsMax; /*!< Linked to MAX_NUMBER_OF_OUTPUT_CHANNELS  */
    370   UCHAR frameDelay;        /*!< Linked to DMX_BS_DATA_DELAY              */
    371 
    372 } PCM_DMX_USER_PARAMS;
    373 
    374 /* Modules main data structure: */
    375 struct PCM_DMX_INSTANCE {
    376   /* Metadata */
    377   DMX_BS_META_DATA bsMetaData[(1) + 1];
    378   PCM_DMX_USER_PARAMS userParams;
    379 
    380   UCHAR applyProcessing; /*!< Flag to en-/disable modules processing.
    381                               The max channel limiting is done independently. */
    382 };
    383 
    384 /* Memory allocation macro */
    385 C_ALLOC_MEM(PcmDmxInstance, struct PCM_DMX_INSTANCE, 1)
    386 
    387 static UINT getSpeakerDistance(PCM_DMX_SPEAKER_POSITION posA,
    388                                PCM_DMX_SPEAKER_POSITION posB) {
    389   PCM_DMX_SPEAKER_POSITION diff;
    390 
    391   diff.x = posA.x - posB.x;
    392   diff.y = posA.y - posB.y;
    393   diff.z = posA.z - posB.z;
    394 
    395   return ((diff.x * diff.x) + (diff.y * diff.y) + (diff.z * diff.z));
    396 }
    397 
    398 static PCM_DMX_SPEAKER_POSITION getSpeakerPos(AUDIO_CHANNEL_TYPE chType,
    399                                               UCHAR chIndex, UCHAR numChInGrp) {
    400 #define PCMDMX_SPKR_POS_X_MAX_WIDTH (3)
    401 #define PCMDMX_SPKR_POS_Y_SPREAD (2)
    402 #define PCMDMX_SPKR_POS_Z_SPREAD (2)
    403 
    404   PCM_DMX_SPEAKER_POSITION spkrPos = {0, 0, 0};
    405   AUDIO_CHANNEL_TYPE chGrp = (AUDIO_CHANNEL_TYPE)(chType & 0x0F);
    406   unsigned fHasCenter = numChInGrp & 0x1;
    407   unsigned chGrpWidth = numChInGrp >> 1;
    408   unsigned fIsCenter = 0;
    409   unsigned fIsLfe = (chType == ACT_LFE) ? 1 : 0;
    410   int offset = 0;
    411 
    412   FDK_ASSERT(chIndex < numChInGrp);
    413 
    414   if ((chGrp == ACT_FRONT) && fHasCenter) {
    415     if (chIndex == 0) fIsCenter = 1;
    416     chIndex = (UCHAR)fMax(0, chIndex - 1);
    417   } else if (fHasCenter && (chIndex == numChInGrp - 1)) {
    418     fIsCenter = 1;
    419   }
    420   /* now all even indices are left (-) */
    421   if (!fIsCenter) {
    422     offset = chIndex >> 1;
    423     if ((chGrp > ACT_FRONT) && (chType != ACT_SIDE) && !fIsLfe) {
    424       /* the higher the index the lower the distance to the center position */
    425       offset = chGrpWidth - fHasCenter - offset;
    426     }
    427     if ((chIndex & 0x1) == 0) { /* even */
    428       offset = -(offset + 1);
    429     } else {
    430       offset += 1;
    431     }
    432   }
    433   /* apply the offset */
    434   if (chType == ACT_SIDE) {
    435     spkrPos.x = (offset < 0) ? -PCMDMX_SPKR_POS_X_MAX_WIDTH
    436                              : PCMDMX_SPKR_POS_X_MAX_WIDTH;
    437     spkrPos.y = /* 1x */ PCMDMX_SPKR_POS_Y_SPREAD + (SCHAR)fAbs(offset) - 1;
    438     spkrPos.z = 0;
    439   } else {
    440     unsigned spread =
    441         ((chGrpWidth == 1) && (!fIsLfe)) ? PCMDMX_SPKR_POS_X_MAX_WIDTH - 1 : 1;
    442     spkrPos.x = (SCHAR)offset * (SCHAR)spread;
    443     if (fIsLfe) {
    444       spkrPos.y = 0;
    445       spkrPos.z = SP_Z_LFE;
    446     } else {
    447       spkrPos.y = (SCHAR)fMax((SCHAR)chGrp - 1, 0) * PCMDMX_SPKR_POS_Y_SPREAD;
    448       spkrPos.z = (SCHAR)chType >> 4;
    449       if (spkrPos.z == 2) { /* ACT_BOTTOM */
    450         spkrPos.z = -1;
    451       }
    452       spkrPos.z *= PCMDMX_SPKR_POS_Z_SPREAD;
    453     }
    454   }
    455   return spkrPos;
    456 }
    457 
    458 /** Return the channel mode of a given horizontal channel plain (normal, top,
    459  *bottom) for a given channel configuration. NOTE: This function shall get
    460  *obsolete once the channel mode has been changed to be nonambiguous.
    461  * @param [in] Index of the requested channel plain.
    462  * @param [in] The packed channel mode for the complete channel configuration
    463  *(all plains).
    464  * @param [in] The MPEG-4 channel configuration index which is necessary in
    465  *cases where the (packed) channel mode is ambiguous.
    466  * @returns Returns the packed channel mode of the requested channel plain.
    467  **/
    468 static PCM_DMX_CHANNEL_MODE getChMode4Plain(
    469     const int plainIndex, const PCM_DMX_CHANNEL_MODE totChMode,
    470     const int chCfg) {
    471   PCM_DMX_CHANNEL_MODE plainChMode = totChMode;
    472 
    473   switch (totChMode) {
    474     case CH_MODE_5_0_2_1:
    475       if (chCfg == 14) {
    476         switch (plainIndex) {
    477           case CH_PLAIN_BOTTOM:
    478             plainChMode = (PCM_DMX_CHANNEL_MODE)0x0000;
    479             break;
    480           case CH_PLAIN_TOP:
    481             plainChMode = CH_MODE_2_0_0_0;
    482             break;
    483           case CH_PLAIN_NORMAL:
    484           default:
    485             plainChMode = CH_MODE_3_0_2_1;
    486             break;
    487         }
    488       }
    489       break;
    490     default:
    491       break;
    492   }
    493 
    494   return plainChMode;
    495 }
    496 
    497 static inline UINT getIdxSum(UCHAR numCh) {
    498   UINT result = 0;
    499   int i;
    500   for (i = 1; i < numCh; i += 1) {
    501     result += i;
    502   }
    503   return result;
    504 }
    505 
    506 /** Evaluate a given channel configuration and extract a packed channel mode. In
    507  *addition the function generates a channel offset table for the mapping to the
    508  *internal representation. This function is the inverse to the
    509  *getChannelDescription() routine.
    510  * @param [in] The total number of channels of the given configuration.
    511  * @param [in] Array holding the corresponding channel types for each channel.
    512  * @param [in] Array holding the corresponding channel type indices for each
    513  *channel.
    514  * @param [out] Array where the buffer offsets for each channel are stored into.
    515  * @param [out] The generated packed channel mode that represents the given
    516  *input configuration.
    517  * @returns Returns an error code.
    518  **/
    519 static PCMDMX_ERROR getChannelMode(
    520     const UINT numChannels,                 /* in */
    521     const AUDIO_CHANNEL_TYPE channelType[], /* in */
    522     UCHAR channelIndices[],                 /* in */
    523     UCHAR offsetTable[(8)],                 /* out */
    524     PCM_DMX_CHANNEL_MODE *chMode            /* out */
    525 ) {
    526   UINT idxSum[(3)][(4)];
    527   UCHAR numCh[(3)][(4)];
    528   UCHAR mapped[(8)];
    529   PCM_DMX_SPEAKER_POSITION spkrPos[(8)];
    530   PCMDMX_ERROR err = PCMDMX_OK;
    531   unsigned ch, numMappedInChs = 0;
    532   unsigned startSlot;
    533   unsigned stopSlot = LOW_FREQUENCY_CHANNEL;
    534 
    535   FDK_ASSERT(channelType != NULL);
    536   FDK_ASSERT(channelIndices != NULL);
    537   FDK_ASSERT(offsetTable != NULL);
    538   FDK_ASSERT(chMode != NULL);
    539 
    540   /* For details see ISO/IEC 13818-7:2005(E), 8.5.3 Channel configuration */
    541   FDKmemclear(idxSum, (3) * (4) * sizeof(UINT));
    542   FDKmemclear(numCh, (3) * (4) * sizeof(UCHAR));
    543   FDKmemclear(mapped, (8) * sizeof(UCHAR));
    544   FDKmemclear(spkrPos, (8) * sizeof(PCM_DMX_SPEAKER_POSITION));
    545   /* Init output */
    546   FDKmemset(offsetTable, 255, (8) * sizeof(UCHAR));
    547   *chMode = CH_MODE_UNDEFINED;
    548 
    549   /* Determine how many channels are assigned to each channels each group: */
    550   for (ch = 0; ch < numChannels; ch += 1) {
    551     unsigned chGrp = fMax(
    552         (channelType[ch] & 0x0F) - 1,
    553         0); /* Assign all undefined channels (ACT_NONE) to front channels. */
    554     numCh[channelType[ch] >> 4][chGrp] += 1;
    555     idxSum[channelType[ch] >> 4][chGrp] += channelIndices[ch];
    556   }
    557   if (numChannels > TWO_CHANNEL) {
    558     int chGrp;
    559     /* Sanity check on the indices */
    560     for (chGrp = 0; chGrp < (4); chGrp += 1) {
    561       int plane;
    562       for (plane = 0; plane < (3); plane += 1) {
    563         if (idxSum[plane][chGrp] != getIdxSum(numCh[plane][chGrp])) {
    564           unsigned idxCnt = 0;
    565           for (ch = 0; ch < numChannels; ch += 1) {
    566             if (channelType[ch] ==
    567                 (AUDIO_CHANNEL_TYPE)((plane << 4) | ((chGrp + 1) & 0xF))) {
    568               channelIndices[ch] = idxCnt++;
    569             }
    570           }
    571           err = PCMDMX_INVALID_CH_CONFIG;
    572         }
    573       }
    574     }
    575   }
    576   /* Mapping HEAT 1:
    577    *   Determine the speaker position of each input channel and map it to a
    578    * internal slot if it matches exactly (with zero distance). */
    579   for (ch = 0; ch < numChannels; ch += 1) {
    580     UINT mapDist = (unsigned)-1;
    581     unsigned mapCh, mapPos = (unsigned)-1;
    582     unsigned chGrp = fMax(
    583         (channelType[ch] & 0x0F) - 1,
    584         0); /* Assign all undefined channels (ACT_NONE) to front channels. */
    585 
    586     spkrPos[ch] = getSpeakerPos(channelType[ch], channelIndices[ch],
    587                                 numCh[channelType[ch] >> 4][chGrp]);
    588 
    589     for (mapCh = 0; mapCh <= stopSlot; mapCh += 1) {
    590       if (offsetTable[mapCh] == 255) {
    591         UINT dist = getSpeakerDistance(spkrPos[ch], spkrSlotPos[mapCh]);
    592         if (dist < mapDist) {
    593           mapPos = mapCh;
    594           mapDist = dist;
    595         }
    596       }
    597     }
    598     if (mapDist <= PCMDMX_THRESHOLD_MAP_HEAT_1) {
    599       offsetTable[mapPos] = (UCHAR)ch;
    600       mapped[ch] = 1;
    601       numMappedInChs += 1;
    602     }
    603   }
    604 
    605   /* Mapping HEAT 2:
    606    *   Go through the unmapped input channels and assign them to the internal
    607    * slots that matches best (least distance). But assign center channels to
    608    * center slots only. */
    609   startSlot =
    610       ((numCh[CH_PLAIN_NORMAL][CH_GROUP_FRONT] & 0x1) || (numChannels >= (8)))
    611           ? 0
    612           : 1;
    613   for (ch = 0; ch < (unsigned)numChannels; ch += 1) {
    614     if (!mapped[ch]) {
    615       UINT mapDist = (unsigned)-1;
    616       unsigned mapCh, mapPos = (unsigned)-1;
    617 
    618       for (mapCh = startSlot; mapCh <= stopSlot; mapCh += 1) {
    619         if (offsetTable[mapCh] == 255) {
    620           UINT dist = getSpeakerDistance(spkrPos[ch], spkrSlotPos[mapCh]);
    621           if (dist < mapDist) {
    622             mapPos = mapCh;
    623             mapDist = dist;
    624           }
    625         }
    626       }
    627       if ((mapPos <= stopSlot) && (mapDist < PCMDMX_THRESHOLD_MAP_HEAT_2) &&
    628           (((spkrPos[ch].x != 0) && (spkrSlotPos[mapPos].x != 0)) /* XOR */
    629            || ((spkrPos[ch].x == 0) &&
    630                (spkrSlotPos[mapPos].x ==
    631                 0)))) { /* Assign center channels to center slots only. */
    632         offsetTable[mapPos] = (UCHAR)ch;
    633         mapped[ch] = 1;
    634         numMappedInChs += 1;
    635       }
    636     }
    637   }
    638 
    639   /* Mapping HEAT 3:
    640    *   Assign the rest by searching for the nearest input channel for each
    641    * internal slot. */
    642   for (ch = startSlot; (ch < (8)) && (numMappedInChs < numChannels); ch += 1) {
    643     if (offsetTable[ch] == 255) {
    644       UINT mapDist = (unsigned)-1;
    645       unsigned mapCh, mapPos = (unsigned)-1;
    646 
    647       for (mapCh = 0; mapCh < (unsigned)numChannels; mapCh += 1) {
    648         if (!mapped[mapCh]) {
    649           UINT dist = getSpeakerDistance(spkrPos[mapCh], spkrSlotPos[ch]);
    650           if (dist < mapDist) {
    651             mapPos = mapCh;
    652             mapDist = dist;
    653           }
    654         }
    655       }
    656       if (mapDist < PCMDMX_THRESHOLD_MAP_HEAT_3) {
    657         offsetTable[ch] = (UCHAR)mapPos;
    658         mapped[mapPos] = 1;
    659         numMappedInChs += 1;
    660         if ((spkrPos[mapPos].x == 0) && (spkrSlotPos[ch].x != 0) &&
    661             (numChannels <
    662              (8))) { /* Skip the paired slot if we assigned a center channel. */
    663           ch += 1;
    664         }
    665       }
    666     }
    667   }
    668 
    669   /* Finaly compose the channel mode */
    670   for (ch = 0; ch < (4); ch += 1) {
    671     int plane, numChInGrp = 0;
    672     for (plane = 0; plane < (3); plane += 1) {
    673       numChInGrp += numCh[plane][ch];
    674     }
    675     *chMode = (PCM_DMX_CHANNEL_MODE)(*chMode | (numChInGrp << (ch * 4)));
    676   }
    677 
    678   return err;
    679 }
    680 
    681 /** Generate a channel offset table and complete channel description for a given
    682  *(packed) channel mode. This function is the inverse to the getChannelMode()
    683  *routine but does not support weird channel configurations.
    684  * @param [in] The packed channel mode of the configuration to be processed.
    685  * @param [in] Array containing the channel mapping to be used (From MPEG PCE
    686  *ordering to whatever is required).
    687  * @param [out] Array where corresponding channel types for each channels are
    688  *stored into.
    689  * @param [out] Array where corresponding channel type indices for each output
    690  *channel are stored into.
    691  * @param [out] Array where the buffer offsets for each channel are stored into.
    692  * @returns None.
    693  **/
    694 static void getChannelDescription(
    695     const PCM_DMX_CHANNEL_MODE chMode,         /* in */
    696     const FDK_channelMapDescr *const mapDescr, /* in */
    697     AUDIO_CHANNEL_TYPE channelType[],          /* out */
    698     UCHAR channelIndices[],                    /* out */
    699     UCHAR offsetTable[(8)]                     /* out */
    700 ) {
    701   int grpIdx, plainIdx, numPlains = 1, numTotalChannels = 0;
    702   int chCfg, ch = 0;
    703 
    704   FDK_ASSERT(channelType != NULL);
    705   FDK_ASSERT(channelIndices != NULL);
    706   FDK_ASSERT(mapDescr != NULL);
    707   FDK_ASSERT(offsetTable != NULL);
    708 
    709   /* Init output arrays */
    710   FDKmemclear(channelType, (8) * sizeof(AUDIO_CHANNEL_TYPE));
    711   FDKmemclear(channelIndices, (8) * sizeof(UCHAR));
    712   FDKmemset(offsetTable, 255, (8) * sizeof(UCHAR));
    713 
    714   /* Summerize to get the total number of channels */
    715   for (grpIdx = 0; grpIdx < (4); grpIdx += 1) {
    716     numTotalChannels += (chMode >> (grpIdx * 4)) & 0xF;
    717   }
    718 
    719   /* Get the appropriate channel map */
    720   switch (chMode) {
    721     case CH_MODE_1_0_0_0:
    722     case CH_MODE_2_0_0_0:
    723     case CH_MODE_3_0_0_0:
    724     case CH_MODE_3_0_1_0:
    725     case CH_MODE_3_0_2_0:
    726     case CH_MODE_3_0_2_1:
    727       chCfg = numTotalChannels;
    728       break;
    729     case CH_MODE_3_0_3_1:
    730       chCfg = 11;
    731       break;
    732     case CH_MODE_3_0_4_1:
    733       chCfg = 12;
    734       break;
    735     case CH_MODE_5_0_2_1:
    736       chCfg = 7;
    737       break;
    738     default:
    739       /* fallback */
    740       chCfg = 0;
    741       break;
    742   }
    743 
    744   /* Compose channel offset table */
    745 
    746   for (plainIdx = 0; plainIdx < numPlains; plainIdx += 1) {
    747     PCM_DMX_CHANNEL_MODE plainChMode;
    748     UCHAR numChInGrp[(4)];
    749 
    750     plainChMode = getChMode4Plain(plainIdx, chMode, chCfg);
    751 
    752     /* Extract the number of channels per group */
    753     numChInGrp[CH_GROUP_FRONT] = plainChMode & 0xF;
    754     numChInGrp[CH_GROUP_SIDE] = (plainChMode >> 4) & 0xF;
    755     numChInGrp[CH_GROUP_REAR] = (plainChMode >> 8) & 0xF;
    756     numChInGrp[CH_GROUP_LFE] = (plainChMode >> 12) & 0xF;
    757 
    758     /* Non-symmetric channels */
    759     if ((numChInGrp[CH_GROUP_FRONT] & 0x1) && (plainIdx == CH_PLAIN_NORMAL)) {
    760       /* Odd number of front channels -> we have a center channel.
    761          In MPEG-4 the center has the index 0. */
    762       int mappedIdx = FDK_chMapDescr_getMapValue(mapDescr, (UCHAR)ch, chCfg);
    763       offsetTable[CENTER_FRONT_CHANNEL] = (UCHAR)mappedIdx;
    764       channelType[mappedIdx] = ACT_FRONT;
    765       channelIndices[mappedIdx] = 0;
    766       ch += 1;
    767     }
    768 
    769     for (grpIdx = 0; grpIdx < (4); grpIdx += 1) {
    770       AUDIO_CHANNEL_TYPE type = ACT_NONE;
    771       int chMapPos = 0, maxChannels = 0;
    772       int chIdx = 0; /* Index of channel within the specific group */
    773 
    774       switch (grpIdx) {
    775         case CH_GROUP_FRONT:
    776           type = (AUDIO_CHANNEL_TYPE)((plainIdx << 4) | ACT_FRONT);
    777           switch (plainIdx) {
    778             default:
    779               chMapPos = LEFT_FRONT_CHANNEL;
    780               chIdx = numChInGrp[grpIdx] & 0x1;
    781               break;
    782           }
    783           maxChannels = 3;
    784           break;
    785         case CH_GROUP_SIDE:
    786           /* Always map side channels to the multipurpose group. */
    787           type = (AUDIO_CHANNEL_TYPE)((plainIdx << 4) | ACT_SIDE);
    788           if (plainIdx == CH_PLAIN_TOP) {
    789             chMapPos = LEFT_SIDE_CHANNEL_TOP;
    790             maxChannels = 3;
    791           } else {
    792             chMapPos = LEFT_MULTIPRPS_CHANNEL;
    793             maxChannels = 2;
    794           }
    795           break;
    796         case CH_GROUP_REAR:
    797           type = (AUDIO_CHANNEL_TYPE)((plainIdx << 4) | ACT_BACK);
    798           if (plainIdx == CH_PLAIN_TOP) {
    799             chMapPos = LEFT_REAR_CHANNEL_TOP;
    800             maxChannels = 3;
    801           } else {
    802             chMapPos = LEFT_REAR_CHANNEL;
    803             maxChannels = 2;
    804           }
    805           break;
    806         case CH_GROUP_LFE:
    807           if (plainIdx == CH_PLAIN_NORMAL) {
    808             type = ACT_LFE;
    809             chMapPos = LOW_FREQUENCY_CHANNEL;
    810             maxChannels = 1;
    811           }
    812           break;
    813         default:
    814           break;
    815       }
    816 
    817       /* Map all channels in this group */
    818       for (; chIdx < numChInGrp[grpIdx]; chIdx += 1) {
    819         int mappedIdx = FDK_chMapDescr_getMapValue(mapDescr, (UCHAR)ch, chCfg);
    820         if ((chIdx == maxChannels) || (offsetTable[chMapPos] < 255)) {
    821           /* No space left in this channel group! */
    822           if (offsetTable[LEFT_MULTIPRPS_CHANNEL] ==
    823               255) { /* Use the multipurpose group: */
    824             chMapPos = LEFT_MULTIPRPS_CHANNEL;
    825           } else {
    826             FDK_ASSERT(0);
    827           }
    828         }
    829         offsetTable[chMapPos] = (UCHAR)mappedIdx;
    830         channelType[mappedIdx] = type;
    831         channelIndices[mappedIdx] = (UCHAR)chIdx;
    832         chMapPos += 1;
    833         ch += 1;
    834       }
    835     }
    836   }
    837 }
    838 
    839 /** Private helper function for downmix matrix manipulation that initializes
    840  *  one row in a given downmix matrix (corresponding to one output channel).
    841  * @param [inout] Pointer to fixed-point parts of the downmix matrix.
    842  * @param [inout] Pointer to scale factor matrix associated to the downmix
    843  *factors.
    844  * @param [in]    Index of channel (row) to be initialized.
    845  * @returns       Nothing to return.
    846  **/
    847 static void dmxInitChannel(FIXP_DMX mixFactors[(8)][(8)],
    848                            INT mixScales[(8)][(8)], const unsigned int outCh) {
    849   unsigned int inCh;
    850   for (inCh = 0; inCh < (8); inCh += 1) {
    851     if (inCh == outCh) {
    852       mixFactors[outCh][inCh] = FL2FXCONST_DMX(0.5f);
    853       mixScales[outCh][inCh] = 1;
    854     } else {
    855       mixFactors[outCh][inCh] = FL2FXCONST_DMX(0.0f);
    856       mixScales[outCh][inCh] = 0;
    857     }
    858   }
    859 }
    860 
    861 /** Private helper function for downmix matrix manipulation that does a reset
    862  *  of one row in a given downmix matrix (corresponding to one output channel).
    863  * @param [inout] Pointer to fixed-point parts of the downmix matrix.
    864  * @param [inout] Pointer to scale factor matrix associated to the downmix
    865  *factors.
    866  * @param [in]    Index of channel (row) to be cleared/reset.
    867  * @returns       Nothing to return.
    868  **/
    869 static void dmxClearChannel(FIXP_DMX mixFactors[(8)][(8)],
    870                             INT mixScales[(8)][(8)], const unsigned int outCh) {
    871   FDK_ASSERT((outCh >= 0) && (outCh < (8)));
    872   FDKmemclear(&mixFactors[outCh], (8) * sizeof(FIXP_DMX));
    873   FDKmemclear(&mixScales[outCh], (8) * sizeof(INT));
    874 }
    875 
    876 /** Private helper function for downmix matrix manipulation that applies a
    877  *source channel (row) scaled by a given mix factor to a destination channel
    878  *(row) in a given downmix matrix. Existing mix factors of the destination
    879  *channel (row) will get overwritten.
    880  * @param [inout] Pointer to fixed-point parts of the downmix matrix.
    881  * @param [inout] Pointer to scale factor matrix associated to the downmix
    882  *factors.
    883  * @param [in]    Index of source channel (row).
    884  * @param [in]    Index of destination channel (row).
    885  * @param [in]    Fixed-point part of mix factor to be applied.
    886  * @param [in]    Scale factor of mix factor to be applied.
    887  * @returns       Nothing to return.
    888  **/
    889 static void dmxSetChannel(FIXP_DMX mixFactors[(8)][(8)],
    890                           INT mixScales[(8)][(8)], const unsigned int dstCh,
    891                           const unsigned int srcCh, const FIXP_DMX factor,
    892                           const INT scale) {
    893   int ch;
    894   for (ch = 0; ch < (8); ch += 1) {
    895     if (mixFactors[srcCh][ch] != (FIXP_DMX)0) {
    896       mixFactors[dstCh][ch] =
    897           FX_DBL2FX_DMX(fMult(mixFactors[srcCh][ch], factor));
    898       mixScales[dstCh][ch] = mixScales[srcCh][ch] + scale;
    899     }
    900   }
    901 }
    902 
    903 /** Private helper function for downmix matrix manipulation that adds a source
    904  *channel (row) scaled by a given mix factor to a destination channel (row) in a
    905  *given downmix matrix.
    906  * @param [inout] Pointer to fixed-point parts of the downmix matrix.
    907  * @param [inout] Pointer to scale factor matrix associated to the downmix
    908  *factors.
    909  * @param [in]    Index of source channel (row).
    910  * @param [in]    Index of destination channel (row).
    911  * @param [in]    Fixed-point part of mix factor to be applied.
    912  * @param [in]    Scale factor of mix factor to be applied.
    913  * @returns       Nothing to return.
    914  **/
    915 static void dmxAddChannel(FIXP_DMX mixFactors[(8)][(8)],
    916                           INT mixScales[(8)][(8)], const unsigned int dstCh,
    917                           const unsigned int srcCh, const FIXP_DMX factor,
    918                           const INT scale) {
    919   int ch;
    920   for (ch = 0; ch < (8); ch += 1) {
    921     FIXP_DBL addFact = fMult(mixFactors[srcCh][ch], factor);
    922     if (addFact != (FIXP_DMX)0) {
    923       INT newScale = mixScales[srcCh][ch] + scale;
    924       if (mixFactors[dstCh][ch] != (FIXP_DMX)0) {
    925         if (newScale > mixScales[dstCh][ch]) {
    926           mixFactors[dstCh][ch] >>= newScale - mixScales[dstCh][ch];
    927         } else {
    928           addFact >>= mixScales[dstCh][ch] - newScale;
    929           newScale = mixScales[dstCh][ch];
    930         }
    931       }
    932       mixFactors[dstCh][ch] += FX_DBL2FX_DMX(addFact);
    933       mixScales[dstCh][ch] = newScale;
    934     }
    935   }
    936 }
    937 
    938 /** Private function that creates a downmix factor matrix depending on the input
    939  and output
    940  *  configuration, the user parameters as well as the given metadata. This
    941  function is the modules
    942  *  brain and hold all downmix algorithms.
    943  * @param [in]  Flag that indicates if inChMode holds a real (packed) channel
    944  mode or has been converted to a MPEG-4 channel configuration index.
    945  * @param [in]  Dependent on the inModeIsCfg flag this field hands in a (packed)
    946  channel mode or the corresponding MPEG-4 channel configuration index.of the
    947  input configuration.
    948  * @param [in]  The (packed) channel mode of the output configuration.
    949  * @param [in]  Pointer to structure holding all current user parameter.
    950  * @param [in]  Pointer to field holding all current meta data.
    951  * @param [out] Pointer to fixed-point parts of the downmix matrix. Normalized
    952  to one scale factor.
    953  * @param [out] The common scale factor of the downmix matrix.
    954  * @returns     An error code.
    955  **/
    956 static PCMDMX_ERROR getMixFactors(const UCHAR inModeIsCfg,
    957                                   PCM_DMX_CHANNEL_MODE inChMode,
    958                                   const PCM_DMX_CHANNEL_MODE outChMode,
    959                                   const PCM_DMX_USER_PARAMS *pParams,
    960                                   const DMX_BS_META_DATA *pMetaData,
    961                                   FIXP_DMX mixFactors[(8)][(8)],
    962                                   INT *pOutScale) {
    963   PCMDMX_ERROR err = PCMDMX_OK;
    964   INT mixScales[(8)][(8)];
    965   INT maxScale = 0;
    966   int numInChannel;
    967   int numOutChannel;
    968   int dmxMethod;
    969   unsigned int outCh, inChCfg = 0;
    970   unsigned int valid[(8)] = {0};
    971 
    972   FDK_ASSERT(pMetaData != NULL);
    973   FDK_ASSERT(mixFactors != NULL);
    974   /* Check on a supported output configuration.
    975      Add new one only after extensive testing! */
    976   if (!((outChMode == CH_MODE_1_0_0_0) || (outChMode == CH_MODE_2_0_0_0) ||
    977         (outChMode == CH_MODE_3_0_2_1) || (outChMode == CH_MODE_3_0_4_1) ||
    978         (outChMode == CH_MODE_5_0_2_1))) {
    979     FDK_ASSERT(0);
    980   }
    981 
    982   if (inModeIsCfg) {
    983     /* Convert channel config to channel mode: */
    984     inChCfg = (unsigned int)inChMode;
    985     switch (inChCfg) {
    986       case 1:
    987       case 2:
    988       case 3:
    989       case 4:
    990       case 5:
    991       case 6:
    992         inChMode = outChModeTable[inChCfg];
    993         break;
    994       case 11:
    995         inChMode = CH_MODE_3_0_3_1;
    996         break;
    997       case 12:
    998         inChMode = CH_MODE_3_0_4_1;
    999         break;
   1000       case 7:
   1001       case 14:
   1002         inChMode = CH_MODE_5_0_2_1;
   1003         break;
   1004       default:
   1005         FDK_ASSERT(0);
   1006     }
   1007   }
   1008 
   1009   /* Extract the total number of input channels */
   1010   numInChannel = (inChMode & 0xF) + ((inChMode >> 4) & 0xF) +
   1011                  ((inChMode >> 8) & 0xF) + ((inChMode >> 12) & 0xF);
   1012   /* Extract the total number of output channels */
   1013   numOutChannel = (outChMode & 0xF) + ((outChMode >> 4) & 0xF) +
   1014                   ((outChMode >> 8) & 0xF) + ((outChMode >> 12) & 0xF);
   1015 
   1016   /* MPEG ammendment 4 aka ETSI metadata and fallback mode: */
   1017 
   1018   /* Create identity DMX matrix: */
   1019   for (outCh = 0; outCh < (8); outCh += 1) {
   1020     dmxInitChannel(mixFactors, mixScales, outCh);
   1021   }
   1022   if (((inChMode >> 12) & 0xF) == 0) {
   1023     /* Clear empty or wrongly mapped input channel */
   1024     dmxClearChannel(mixFactors, mixScales, LOW_FREQUENCY_CHANNEL);
   1025   }
   1026 
   1027   /* FIRST STAGE: */
   1028   if (numInChannel > SIX_CHANNEL) { /* Always use MPEG equations either with
   1029                                        meta data or with default values. */
   1030     FIXP_DMX dMixFactA, dMixFactB;
   1031     INT dMixScaleA, dMixScaleB;
   1032     int isValidCfg = TRUE;
   1033 
   1034     /* Get factors from meta data */
   1035     dMixFactA = abMixLvlValueTab[pMetaData->dmixIdxA];
   1036     dMixScaleA = (pMetaData->dmixIdxA == 0) ? 1 : 0;
   1037     dMixFactB = abMixLvlValueTab[pMetaData->dmixIdxB];
   1038     dMixScaleB = (pMetaData->dmixIdxB == 0) ? 1 : 0;
   1039 
   1040     /* Check if input is in the list of supported configurations */
   1041     switch (inChMode) {
   1042       case CH_MODE_3_2_1_1: /* chCfg 11 but with side channels */
   1043       case CH_MODE_3_2_1_0:
   1044         isValidCfg = FALSE;
   1045         err = PCMDMX_INVALID_MODE;
   1046         FDK_FALLTHROUGH;
   1047       case CH_MODE_3_0_3_1: /* chCfg 11 */
   1048         /* 6.1ch:  C' = C;  L' = L;  R' = R;  LFE' = LFE;
   1049                    Ls' = Ls*dmix_a_idx + Cs*dmix_b_idx;
   1050                    Rs' = Rs*dmix_a_idx + Cs*dmix_b_idx; */
   1051         dmxClearChannel(
   1052             mixFactors, mixScales,
   1053             RIGHT_MULTIPRPS_CHANNEL); /* clear empty input channel */
   1054         dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL,
   1055                       LEFT_REAR_CHANNEL, dMixFactA, dMixScaleA);
   1056         dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL,
   1057                       LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1058         dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1059                       RIGHT_REAR_CHANNEL, dMixFactA, dMixScaleA);
   1060         dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1061                       LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1062         break;
   1063       case CH_MODE_3_0_4_1: /* chCfg 12 */
   1064         /* 7.1ch Surround Back:  C' = C;  L' = L;  R' = R;  LFE' = LFE;
   1065                                  Ls' = Ls*dmix_a_idx + Lsr*dmix_b_idx;
   1066                                  Rs' = Rs*dmix_a_idx + Rsr*dmix_b_idx; */
   1067         dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL,
   1068                       LEFT_REAR_CHANNEL, dMixFactA, dMixScaleA);
   1069         dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL,
   1070                       LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1071         dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1072                       RIGHT_REAR_CHANNEL, dMixFactA, dMixScaleA);
   1073         dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1074                       RIGHT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1075         break;
   1076       case CH_MODE_5_0_1_0:
   1077       case CH_MODE_5_0_1_1:
   1078         dmxClearChannel(mixFactors, mixScales,
   1079                         RIGHT_REAR_CHANNEL); /* clear empty input channel */
   1080         dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1081                       LEFT_REAR_CHANNEL, FL2FXCONST_DMX(0.5f), 1);
   1082         dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL,
   1083                       LEFT_REAR_CHANNEL, FL2FXCONST_DMX(0.5f), 1);
   1084         FDK_FALLTHROUGH;
   1085       case CH_MODE_5_2_1_0:
   1086         isValidCfg = FALSE;
   1087         err = PCMDMX_INVALID_MODE;
   1088         FDK_FALLTHROUGH;
   1089       case CH_MODE_5_0_2_1: /* chCfg 7 || 14 */
   1090         if (inChCfg == 14) {
   1091           /* 7.1ch Front Height:  C' = C;  Ls' = Ls;  Rs' = Rs;  LFE' = LFE;
   1092                                   L' = L*dmix_a_idx + Lv*dmix_b_idx;
   1093                                   R' = R*dmix_a_idx + Rv*dmix_b_idx; */
   1094           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1095                         LEFT_FRONT_CHANNEL, dMixFactA, dMixScaleA);
   1096           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1097                         LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1098           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1099                         RIGHT_FRONT_CHANNEL, dMixFactA, dMixScaleA);
   1100           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1101                         RIGHT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1102         } else {
   1103           /* 7.1ch Front:  Ls' = Ls;  Rs' = Rs;  LFE' = LFE;
   1104                            C' = C + (Lc+Rc)*dmix_a_idx;
   1105                            L' = L + Lc*dmix_b_idx;
   1106                            R' = R + Rc*dmix_b_idx; */
   1107           dmxSetChannel(mixFactors, mixScales, CENTER_FRONT_CHANNEL,
   1108                         LEFT_MULTIPRPS_CHANNEL, dMixFactA, dMixScaleA);
   1109           dmxSetChannel(mixFactors, mixScales, CENTER_FRONT_CHANNEL,
   1110                         RIGHT_MULTIPRPS_CHANNEL, dMixFactA, dMixScaleA);
   1111           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1112                         LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1113           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1114                         LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 1);
   1115           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1116                         RIGHT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1117           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1118                         RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 1);
   1119         }
   1120         break;
   1121       default:
   1122         /* Nothing to do. Just use the identity matrix. */
   1123         isValidCfg = FALSE;
   1124         err = PCMDMX_INVALID_MODE;
   1125         break;
   1126     }
   1127 
   1128     /* Add additional DMX gain */
   1129     if ((isValidCfg == TRUE) &&
   1130         (pMetaData->dmxGainIdx5 != 0)) { /* Apply DMX gain 5 */
   1131       FIXP_DMX dmxGain;
   1132       INT dmxScale;
   1133       INT sign = (pMetaData->dmxGainIdx5 & 0x40) ? -1 : 1;
   1134       INT val = pMetaData->dmxGainIdx5 & 0x3F;
   1135 
   1136       /* 10^(dmx_gain_5/80) */
   1137       dmxGain = FX_DBL2FX_DMX(
   1138           fLdPow(FL2FXCONST_DBL(0.830482023721841f), 2, /* log2(10) */
   1139                  (FIXP_DBL)(sign * val * (LONG)FL2FXCONST_DBL(0.0125f)), 0,
   1140                  &dmxScale));
   1141       /* Currently only positive scale factors supported! */
   1142       if (dmxScale < 0) {
   1143         dmxGain >>= -dmxScale;
   1144         dmxScale = 0;
   1145       }
   1146 
   1147       dmxSetChannel(mixFactors, mixScales, CENTER_FRONT_CHANNEL,
   1148                     CENTER_FRONT_CHANNEL, dmxGain, dmxScale);
   1149       dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1150                     LEFT_FRONT_CHANNEL, dmxGain, dmxScale);
   1151       dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1152                     RIGHT_FRONT_CHANNEL, dmxGain, dmxScale);
   1153       dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL, LEFT_REAR_CHANNEL,
   1154                     dmxGain, dmxScale);
   1155       dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1156                     RIGHT_REAR_CHANNEL, dmxGain, dmxScale);
   1157       dmxSetChannel(mixFactors, mixScales, LOW_FREQUENCY_CHANNEL,
   1158                     LOW_FREQUENCY_CHANNEL, dmxGain, dmxScale);
   1159     }
   1160 
   1161     /* Mark the output channels */
   1162     valid[CENTER_FRONT_CHANNEL] = 1;
   1163     valid[LEFT_FRONT_CHANNEL] = 1;
   1164     valid[RIGHT_FRONT_CHANNEL] = 1;
   1165     valid[LEFT_REAR_CHANNEL] = 1;
   1166     valid[RIGHT_REAR_CHANNEL] = 1;
   1167     valid[LOW_FREQUENCY_CHANNEL] = 1;
   1168 
   1169     /* Update channel mode for the next stage */
   1170     inChMode = CH_MODE_3_0_2_1;
   1171   }
   1172 
   1173     /* For the X (> 6) to 6 channel downmix we had no choice.
   1174        To mix from 6 to 2 (or 1) channel(s) we have several possibilities (MPEG
   1175        DSE | MPEG PCE | ITU | ARIB | DLB). Use profile and the metadata
   1176        available flags to determine which equation to use: */
   1177 
   1178 #define DMX_METHOD_MPEG_AMD4 1
   1179 #define DMX_METHOD_MPEG_LEGACY 2
   1180 #define DMX_METHOD_ARIB_JAPAN 4
   1181 #define DMX_METHOD_ITU_RECOM 8
   1182 #define DMX_METHOD_CUSTOM 16
   1183 
   1184   dmxMethod = DMX_METHOD_MPEG_AMD4; /* default */
   1185 
   1186   if ((pParams->dmxProfile == DMX_PRFL_FORCE_MATRIX_MIX) &&
   1187       (pMetaData->typeFlags & TYPE_PCE_DATA)) {
   1188     dmxMethod = DMX_METHOD_MPEG_LEGACY;
   1189   } else if (!(pMetaData->typeFlags &
   1190                (TYPE_DSE_CLEV_DATA | TYPE_DSE_SLEV_DATA))) {
   1191     switch (pParams->dmxProfile) {
   1192       default:
   1193       case DMX_PRFL_STANDARD:
   1194         /* dmxMethod = DMX_METHOD_MPEG_AMD4; */
   1195         break;
   1196       case DMX_PRFL_MATRIX_MIX:
   1197       case DMX_PRFL_FORCE_MATRIX_MIX:
   1198         if (pMetaData->typeFlags & TYPE_PCE_DATA) {
   1199           dmxMethod = DMX_METHOD_MPEG_LEGACY;
   1200         }
   1201         break;
   1202       case DMX_PRFL_ARIB_JAPAN:
   1203         dmxMethod = DMX_METHOD_ARIB_JAPAN;
   1204         break;
   1205     }
   1206   }
   1207 
   1208   /* SECOND STAGE: */
   1209   if (numOutChannel <= TWO_CHANNEL) {
   1210     /* Create DMX matrix according to input configuration */
   1211     switch (inChMode) {
   1212       case CH_MODE_2_0_0_0: /* chCfg 2 */
   1213         /* Apply the dual channel mode. */
   1214         switch (pParams->dualChannelMode) {
   1215           case CH1_MODE: /* L' = 0.707 * Ch1;
   1216                             R' = 0.707 * Ch1; */
   1217             dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1218                           LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1219             dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1220                           LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1221             break;
   1222           case CH2_MODE: /* L' = 0.707 * Ch2;
   1223                             R' = 0.707 * Ch2; */
   1224             dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1225                           RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1226             dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1227                           RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1228             break;
   1229           case MIXED_MODE: /* L' = 0.5*Ch1 + 0.5*Ch2;
   1230                               R' = 0.5*Ch1 + 0.5*Ch2; */
   1231             dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1232                           LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0);
   1233             dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1234                           RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0);
   1235             dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1236                           LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0);
   1237             dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1238                           RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0);
   1239             break;
   1240           default:
   1241           case STEREO_MODE:
   1242             /* Nothing to do */
   1243             break;
   1244         }
   1245         break;
   1246       /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1247        * - - - - - - - - - - - - - - - - - - - */
   1248       case CH_MODE_2_0_1_0: {
   1249         FIXP_DMX sMixLvl;
   1250         if (dmxMethod == DMX_METHOD_ARIB_JAPAN) {
   1251           /* L' = 0.707*L + 0.5*S;  R' = 0.707*R + 0.5*S; */
   1252           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1253                         LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1254           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1255                         RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1256           sMixLvl = FL2FXCONST_DMX(0.5f);
   1257         } else { /* L' = L + 0.707*S;  R' = R + 0.707*S; */
   1258           sMixLvl = FL2FXCONST_DMX(0.707f);
   1259         }
   1260         dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1261                       LEFT_REAR_CHANNEL, sMixLvl, 0);
   1262         dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1263                       LEFT_REAR_CHANNEL, sMixLvl, 0);
   1264       } break;
   1265       /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1266        * - - - - - - - - - - - - - - - - - - - */
   1267       case CH_MODE_3_0_0_0: /* chCfg 3 */
   1268       {
   1269         FIXP_DMX cMixLvl;
   1270         if (dmxMethod == DMX_METHOD_ARIB_JAPAN) {
   1271           /* L' = 0.707*L + 0.5*C;  R' = 0.707*R + 0.5*C; */
   1272           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1273                         LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1274           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1275                         RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1276           cMixLvl = FL2FXCONST_DMX(0.5f);
   1277         } else { /* L' = L + 0.707*C;  R' = R + 0.707*C; */
   1278           cMixLvl = FL2FXCONST_DMX(0.707f);
   1279         }
   1280         dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1281                       CENTER_FRONT_CHANNEL, cMixLvl, 0);
   1282         dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1283                       CENTER_FRONT_CHANNEL, cMixLvl, 0);
   1284       } break;
   1285       /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1286        * - - - - - - - - - - - - - - - - - - - */
   1287       case CH_MODE_3_0_1_0: /* chCfg 4 */
   1288       {
   1289         FIXP_DMX csMixLvl;
   1290         if (dmxMethod == DMX_METHOD_ARIB_JAPAN) {
   1291           /* L' = 0.707*L + 0.5*C + 0.5*S;  R' = 0.707*R + 0.5*C + 0.5*S; */
   1292           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1293                         LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1294           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1295                         RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1296           csMixLvl = FL2FXCONST_DMX(0.5f);
   1297         } else { /* L' = L + 0.707*C + 0.707*S;
   1298                     R' = R + 0.707*C + 0.707*S; */
   1299           csMixLvl = FL2FXCONST_DMX(0.707f);
   1300         }
   1301         dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1302                       CENTER_FRONT_CHANNEL, csMixLvl, 0);
   1303         dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1304                       LEFT_REAR_CHANNEL, csMixLvl, 0);
   1305         dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1306                       CENTER_FRONT_CHANNEL, csMixLvl, 0);
   1307         dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1308                       LEFT_REAR_CHANNEL, csMixLvl, 0);
   1309       } break;
   1310       /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1311        * - - - - - - - - - - - - - - - - - - - */
   1312       case CH_MODE_3_0_2_0: /* chCfg 5 */
   1313       case CH_MODE_3_0_2_1: /* chCfg 6 */
   1314       {
   1315         switch (dmxMethod) {
   1316           default:
   1317           case DMX_METHOD_MPEG_AMD4: {
   1318             FIXP_DMX cMixLvl, sMixLvl, lMixLvl;
   1319             INT cMixScale, sMixScale, lMixScale;
   1320 
   1321             /* Get factors from meta data */
   1322             cMixLvl = abMixLvlValueTab[pMetaData->cLevIdx];
   1323             cMixScale = (pMetaData->cLevIdx == 0) ? 1 : 0;
   1324             sMixLvl = abMixLvlValueTab[pMetaData->sLevIdx];
   1325             sMixScale = (pMetaData->sLevIdx == 0) ? 1 : 0;
   1326             lMixLvl = lfeMixLvlValueTab[pMetaData->dmixIdxLfe];
   1327             if (pMetaData->dmixIdxLfe <= 1) {
   1328               lMixScale = 2;
   1329             } else if (pMetaData->dmixIdxLfe <= 5) {
   1330               lMixScale = 1;
   1331             } else {
   1332               lMixScale = 0;
   1333             }
   1334             /* Setup the DMX matrix */
   1335             if ((pParams->pseudoSurrMode == FORCE_PS_DMX) ||
   1336                 ((pParams->pseudoSurrMode == AUTO_PS_DMX) &&
   1337                  (pMetaData->pseudoSurround ==
   1338                   1))) { /* L' = L + C*clev - (Ls+Rs)*slev + LFE*lflev;
   1339                             R' = R + C*clev + (Ls+Rs)*slev + LFE*lflev; */
   1340               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1341                             CENTER_FRONT_CHANNEL, cMixLvl, cMixScale);
   1342               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1343                             LEFT_REAR_CHANNEL, -sMixLvl, sMixScale);
   1344               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1345                             RIGHT_REAR_CHANNEL, -sMixLvl, sMixScale);
   1346               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1347                             LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale);
   1348               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1349                             CENTER_FRONT_CHANNEL, cMixLvl, cMixScale);
   1350               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1351                             LEFT_REAR_CHANNEL, sMixLvl, sMixScale);
   1352               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1353                             RIGHT_REAR_CHANNEL, sMixLvl, sMixScale);
   1354               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1355                             LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale);
   1356             } else { /* L' = L + C*clev + Ls*slev + LFE*llev;
   1357                         R' = R + C*clev + Rs*slev + LFE*llev; */
   1358               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1359                             CENTER_FRONT_CHANNEL, cMixLvl, cMixScale);
   1360               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1361                             LEFT_REAR_CHANNEL, sMixLvl, sMixScale);
   1362               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1363                             LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale);
   1364               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1365                             CENTER_FRONT_CHANNEL, cMixLvl, cMixScale);
   1366               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1367                             RIGHT_REAR_CHANNEL, sMixLvl, sMixScale);
   1368               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1369                             LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale);
   1370             }
   1371 
   1372             /* Add additional DMX gain */
   1373             if (pMetaData->dmxGainIdx2 != 0) { /* Apply DMX gain 2 */
   1374               FIXP_DMX dmxGain;
   1375               INT dmxScale;
   1376               INT sign = (pMetaData->dmxGainIdx2 & 0x40) ? -1 : 1;
   1377               INT val = pMetaData->dmxGainIdx2 & 0x3F;
   1378 
   1379               /* 10^(dmx_gain_2/80) */
   1380               dmxGain = FX_DBL2FX_DMX(
   1381                   fLdPow(FL2FXCONST_DBL(0.830482023721841f), 2, /* log2(10) */
   1382                          (FIXP_DBL)(sign * val * (LONG)FL2FXCONST_DBL(0.0125f)),
   1383                          0, &dmxScale));
   1384               /* Currently only positive scale factors supported! */
   1385               if (dmxScale < 0) {
   1386                 dmxGain >>= -dmxScale;
   1387                 dmxScale = 0;
   1388               }
   1389 
   1390               dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1391                             LEFT_FRONT_CHANNEL, dmxGain, dmxScale);
   1392               dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1393                             RIGHT_FRONT_CHANNEL, dmxGain, dmxScale);
   1394             }
   1395           } break;
   1396           case DMX_METHOD_ARIB_JAPAN:
   1397           case DMX_METHOD_MPEG_LEGACY: {
   1398             FIXP_DMX flev, clev, slevLL, slevLR, slevRL, slevRR;
   1399             FIXP_DMX mtrxMixDwnCoef =
   1400                 mpegMixDownIdx2Coef[pMetaData->matrixMixdownIdx];
   1401 
   1402             if ((pParams->pseudoSurrMode == FORCE_PS_DMX) ||
   1403                 ((pParams->pseudoSurrMode == AUTO_PS_DMX) &&
   1404                  (pMetaData->pseudoSurround == 1))) {
   1405               if (dmxMethod == DMX_METHOD_ARIB_JAPAN) {
   1406                 /* 3/2 input: L' = 0.707 * [L+0.707*C-k*Ls-k*Rs];
   1407                               R' = 0.707 * [R+0.707*C+k*Ls+k*Rs]; */
   1408                 flev = mpegMixDownIdx2Coef[0]; /* a = 0.707 */
   1409               } else { /* 3/2 input: L' = (1.707+2*A)^-1 *
   1410                           [L+0.707*C-A*Ls-A*Rs]; R' = (1.707+2*A)^-1 *
   1411                           [R+0.707*C+A*Ls+A*Rs]; */
   1412                 flev = mpegMixDownIdx2PreFact[1][pMetaData->matrixMixdownIdx];
   1413               }
   1414               slevRR = slevRL = FX_DBL2FX_DMX(fMult(flev, mtrxMixDwnCoef));
   1415               slevLL = slevLR = -slevRL;
   1416             } else {
   1417               if (dmxMethod == DMX_METHOD_ARIB_JAPAN) {
   1418                 /* 3/2 input: L' = 0.707 * [L+0.707*C+k*Ls];
   1419                               R' = 0.707 * [R+0.707*C+k*Rs]; */
   1420                 flev = mpegMixDownIdx2Coef[0]; /* a = 0.707 */
   1421               } else { /* 3/2 input: L' = (1.707+A)^-1 * [L+0.707*C+A*Ls];
   1422                                      R' = (1.707+A)^-1 * [R+0.707*C+A*Rs]; */
   1423                 flev = mpegMixDownIdx2PreFact[0][pMetaData->matrixMixdownIdx];
   1424               }
   1425               slevRR = slevLL = FX_DBL2FX_DMX(fMult(flev, mtrxMixDwnCoef));
   1426               slevLR = slevRL = (FIXP_DMX)0;
   1427             }
   1428             /* common factor */
   1429             clev =
   1430                 FX_DBL2FX_DMX(fMult(flev, mpegMixDownIdx2Coef[0] /* 0.707 */));
   1431 
   1432             dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1433                           LEFT_FRONT_CHANNEL, flev, 0);
   1434             dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1435                           CENTER_FRONT_CHANNEL, clev, 0);
   1436             dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1437                           LEFT_REAR_CHANNEL, slevLL, 0);
   1438             dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1439                           RIGHT_REAR_CHANNEL, slevLR, 0);
   1440 
   1441             dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1442                           RIGHT_FRONT_CHANNEL, flev, 0);
   1443             dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1444                           CENTER_FRONT_CHANNEL, clev, 0);
   1445             dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1446                           LEFT_REAR_CHANNEL, slevRL, 0);
   1447             dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1448                           RIGHT_REAR_CHANNEL, slevRR, 0);
   1449           } break;
   1450         } /* switch (dmxMethod) */
   1451       } break;
   1452       default:
   1453         /* This configuration does not fit to any known downmix equation! */
   1454         err = PCMDMX_INVALID_MODE;
   1455         break;
   1456     } /* switch (inChMode) */
   1457 
   1458     /* Mark the output channels */
   1459     FDKmemclear(valid, (8) * sizeof(unsigned int));
   1460     valid[LEFT_FRONT_CHANNEL] = 1;
   1461     valid[RIGHT_FRONT_CHANNEL] = 1;
   1462   }
   1463 
   1464   if (numOutChannel == ONE_CHANNEL) {
   1465     FIXP_DMX monoMixLevel;
   1466     INT monoMixScale = 0;
   1467 
   1468     dmxClearChannel(mixFactors, mixScales,
   1469                     CENTER_FRONT_CHANNEL); /* C is not in the mix */
   1470 
   1471     if (dmxMethod ==
   1472         DMX_METHOD_MPEG_LEGACY) { /* C' = (3+2*A)^-1 * [C+L+R+A*Ls+A+Rs]; */
   1473       monoMixLevel = mpegMixDownIdx2PreFact[2][pMetaData->matrixMixdownIdx];
   1474 
   1475       mixFactors[CENTER_FRONT_CHANNEL][CENTER_FRONT_CHANNEL] = monoMixLevel;
   1476       mixFactors[CENTER_FRONT_CHANNEL][LEFT_FRONT_CHANNEL] = monoMixLevel;
   1477       mixFactors[CENTER_FRONT_CHANNEL][RIGHT_FRONT_CHANNEL] = monoMixLevel;
   1478       monoMixLevel = FX_DBL2FX_DMX(fMult(
   1479           monoMixLevel, mpegMixDownIdx2Coef[pMetaData->matrixMixdownIdx]));
   1480       mixFactors[CENTER_FRONT_CHANNEL][LEFT_REAR_CHANNEL] = monoMixLevel;
   1481       mixFactors[CENTER_FRONT_CHANNEL][RIGHT_REAR_CHANNEL] = monoMixLevel;
   1482     } else {
   1483       switch (dmxMethod) {
   1484         case DMX_METHOD_MPEG_AMD4:
   1485           /* C' = L + R; */
   1486           monoMixLevel = FL2FXCONST_DMX(0.5f);
   1487           monoMixScale = 1;
   1488           break;
   1489         default:
   1490           /* C' = 0.5*L + 0.5*R; */
   1491           monoMixLevel = FL2FXCONST_DMX(0.5f);
   1492           monoMixScale = 0;
   1493           break;
   1494       }
   1495       dmxSetChannel(mixFactors, mixScales, CENTER_FRONT_CHANNEL,
   1496                     LEFT_FRONT_CHANNEL, monoMixLevel, monoMixScale);
   1497       dmxAddChannel(mixFactors, mixScales, CENTER_FRONT_CHANNEL,
   1498                     RIGHT_FRONT_CHANNEL, monoMixLevel, monoMixScale);
   1499     }
   1500 
   1501     /* Mark the output channel */
   1502     FDKmemclear(valid, (8) * sizeof(unsigned int));
   1503     valid[CENTER_FRONT_CHANNEL] = 1;
   1504   }
   1505 
   1506 #define MAX_SEARCH_START_VAL (-7)
   1507 
   1508   {
   1509     LONG chSum[(8)];
   1510     INT chSumMax = MAX_SEARCH_START_VAL;
   1511 
   1512     /* Determine the current maximum scale factor */
   1513     for (outCh = 0; outCh < (8); outCh += 1) {
   1514       if (valid[outCh] != 0) {
   1515         unsigned int inCh;
   1516         for (inCh = 0; inCh < (8); inCh += 1) {
   1517           if (mixScales[outCh][inCh] > maxScale) { /* Store the new maximum */
   1518             maxScale = mixScales[outCh][inCh];
   1519           }
   1520         }
   1521       }
   1522     }
   1523 
   1524     /* Individualy analyse output chanal levels */
   1525     for (outCh = 0; outCh < (8); outCh += 1) {
   1526       chSum[outCh] = MAX_SEARCH_START_VAL;
   1527       if (valid[outCh] != 0) {
   1528         int ovrflwProtScale = 0;
   1529         unsigned int inCh;
   1530 
   1531         /* Accumulate all factors for each output channel */
   1532         chSum[outCh] = 0;
   1533         for (inCh = 0; inCh < (8); inCh += 1) {
   1534           SHORT addFact = FX_DMX2SHRT(mixFactors[outCh][inCh]);
   1535           if (mixScales[outCh][inCh] <= maxScale) {
   1536             addFact >>= maxScale - mixScales[outCh][inCh];
   1537           } else {
   1538             addFact <<= mixScales[outCh][inCh] - maxScale;
   1539           }
   1540           chSum[outCh] += addFact;
   1541         }
   1542         if (chSum[outCh] > (LONG)MAXVAL_SGL) {
   1543           while (chSum[outCh] > (LONG)MAXVAL_SGL) {
   1544             ovrflwProtScale += 1;
   1545             chSum[outCh] >>= 1;
   1546           }
   1547         } else if (chSum[outCh] > 0) {
   1548           while ((chSum[outCh] << 1) <= (LONG)MAXVAL_SGL) {
   1549             ovrflwProtScale -= 1;
   1550             chSum[outCh] <<= 1;
   1551           }
   1552         }
   1553         /* Store the differential scaling in the same array */
   1554         chSum[outCh] = ovrflwProtScale;
   1555       }
   1556     }
   1557 
   1558     for (outCh = 0; outCh < (8); outCh += 1) {
   1559       if ((valid[outCh] != 0) &&
   1560           (chSum[outCh] > chSumMax)) { /* Store the new maximum */
   1561         chSumMax = chSum[outCh];
   1562       }
   1563     }
   1564     maxScale = fMax(maxScale + chSumMax, 0);
   1565 
   1566     /* Normalize all factors */
   1567     for (outCh = 0; outCh < (8); outCh += 1) {
   1568       if (valid[outCh] != 0) {
   1569         unsigned int inCh;
   1570         for (inCh = 0; inCh < (8); inCh += 1) {
   1571           if (mixFactors[outCh][inCh] != (FIXP_DMX)0) {
   1572             if (mixScales[outCh][inCh] <= maxScale) {
   1573               mixFactors[outCh][inCh] >>= maxScale - mixScales[outCh][inCh];
   1574             } else {
   1575               mixFactors[outCh][inCh] <<= mixScales[outCh][inCh] - maxScale;
   1576             }
   1577             mixScales[outCh][inCh] = maxScale;
   1578           }
   1579         }
   1580       }
   1581     }
   1582   }
   1583 
   1584   /* return the scale factor */
   1585   *pOutScale = maxScale;
   1586 
   1587   return (err);
   1588 }
   1589 
   1590 /** Open and initialize an instance of the PCM downmix module
   1591  * @param [out] Pointer to a buffer receiving the handle of the new instance.
   1592  * @returns Returns an error code.
   1593  **/
   1594 PCMDMX_ERROR pcmDmx_Open(HANDLE_PCM_DOWNMIX *pSelf) {
   1595   HANDLE_PCM_DOWNMIX self;
   1596 
   1597   if (pSelf == NULL) {
   1598     return (PCMDMX_INVALID_HANDLE);
   1599   }
   1600 
   1601   *pSelf = NULL;
   1602 
   1603   self = (HANDLE_PCM_DOWNMIX)GetPcmDmxInstance(0);
   1604   if (self == NULL) {
   1605     return (PCMDMX_OUT_OF_MEMORY);
   1606   }
   1607 
   1608   /* Reset the full instance */
   1609   pcmDmx_Reset(self, PCMDMX_RESET_FULL);
   1610 
   1611   *pSelf = self;
   1612 
   1613   return (PCMDMX_OK);
   1614 }
   1615 
   1616 /** Reset all static values like e.g. mixdown coefficients.
   1617  * @param [in] Handle of PCM downmix module instance.
   1618  * @param [in] Flags telling which parts of the module shall be reset.
   1619  * @returns Returns an error code.
   1620  **/
   1621 PCMDMX_ERROR pcmDmx_Reset(HANDLE_PCM_DOWNMIX self, UINT flags) {
   1622   if (self == NULL) {
   1623     return (PCMDMX_INVALID_HANDLE);
   1624   }
   1625 
   1626   if (flags & PCMDMX_RESET_PARAMS) {
   1627     PCM_DMX_USER_PARAMS *pParams = &self->userParams;
   1628 
   1629     pParams->dualChannelMode = STEREO_MODE;
   1630     pParams->pseudoSurrMode = NEVER_DO_PS_DMX;
   1631     pParams->numOutChannelsMax = (6);
   1632     pParams->numOutChannelsMin = (0);
   1633     pParams->frameDelay = 0;
   1634     pParams->expiryFrame = (0);
   1635 
   1636     self->applyProcessing = 0;
   1637   }
   1638 
   1639   if (flags & PCMDMX_RESET_BS_DATA) {
   1640     int slot;
   1641     /* Init all slots with a default set */
   1642     for (slot = 0; slot <= (1); slot += 1) {
   1643       FDKmemcpy(&self->bsMetaData[slot], &dfltMetaData,
   1644                 sizeof(DMX_BS_META_DATA));
   1645     }
   1646   }
   1647 
   1648   return (PCMDMX_OK);
   1649 }
   1650 
   1651 /** Set one parameter for one instance of the PCM downmix module.
   1652  * @param [in] Handle of PCM downmix module instance.
   1653  * @param [in] Parameter to be set.
   1654  * @param [in] Parameter value.
   1655  * @returns Returns an error code.
   1656  **/
   1657 PCMDMX_ERROR pcmDmx_SetParam(HANDLE_PCM_DOWNMIX self, const PCMDMX_PARAM param,
   1658                              const INT value) {
   1659   switch (param) {
   1660     case DMX_PROFILE_SETTING:
   1661       switch ((DMX_PROFILE_TYPE)value) {
   1662         case DMX_PRFL_STANDARD:
   1663         case DMX_PRFL_MATRIX_MIX:
   1664         case DMX_PRFL_FORCE_MATRIX_MIX:
   1665         case DMX_PRFL_ARIB_JAPAN:
   1666           break;
   1667         default:
   1668           return (PCMDMX_UNABLE_TO_SET_PARAM);
   1669       }
   1670       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1671       self->userParams.dmxProfile = (DMX_PROFILE_TYPE)value;
   1672       break;
   1673 
   1674     case DMX_BS_DATA_EXPIRY_FRAME:
   1675       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1676       self->userParams.expiryFrame = (value > 0) ? (UINT)value : 0;
   1677       break;
   1678 
   1679     case DMX_BS_DATA_DELAY:
   1680       if ((value > (1)) || (value < 0)) {
   1681         return (PCMDMX_UNABLE_TO_SET_PARAM);
   1682       }
   1683       if (self == NULL) {
   1684         return (PCMDMX_INVALID_HANDLE);
   1685       }
   1686       self->userParams.frameDelay = (UCHAR)value;
   1687       break;
   1688 
   1689     case MIN_NUMBER_OF_OUTPUT_CHANNELS:
   1690       switch (value) { /* supported output channels */
   1691         case -1:
   1692         case 0:
   1693         case ONE_CHANNEL:
   1694         case TWO_CHANNEL:
   1695         case SIX_CHANNEL:
   1696         case EIGHT_CHANNEL:
   1697           break;
   1698         default:
   1699           return (PCMDMX_UNABLE_TO_SET_PARAM);
   1700       }
   1701       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1702       /* Store the new value */
   1703       self->userParams.numOutChannelsMin = (value > 0) ? (SHORT)value : -1;
   1704       if ((value > 0) && (self->userParams.numOutChannelsMax > 0) &&
   1705           (value > self->userParams
   1706                        .numOutChannelsMax)) { /* MIN > MAX would be an invalid
   1707                                                  state. Thus set MAX = MIN in
   1708                                                  this case. */
   1709         self->userParams.numOutChannelsMax = self->userParams.numOutChannelsMin;
   1710       }
   1711       break;
   1712 
   1713     case MAX_NUMBER_OF_OUTPUT_CHANNELS:
   1714       switch (value) { /* supported output channels */
   1715         case -1:
   1716         case 0:
   1717         case ONE_CHANNEL:
   1718         case TWO_CHANNEL:
   1719         case SIX_CHANNEL:
   1720         case EIGHT_CHANNEL:
   1721           break;
   1722         default:
   1723           return (PCMDMX_UNABLE_TO_SET_PARAM);
   1724       }
   1725       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1726       /* Store the new value */
   1727       self->userParams.numOutChannelsMax = (value > 0) ? (SHORT)value : -1;
   1728       if ((value > 0) &&
   1729           (value < self->userParams
   1730                        .numOutChannelsMin)) { /* MAX < MIN would be an invalid
   1731                                                  state. Thus set MIN = MAX in
   1732                                                  this case. */
   1733         self->userParams.numOutChannelsMin = self->userParams.numOutChannelsMax;
   1734       }
   1735       break;
   1736 
   1737     case DMX_DUAL_CHANNEL_MODE:
   1738       switch ((DUAL_CHANNEL_MODE)value) {
   1739         case STEREO_MODE:
   1740         case CH1_MODE:
   1741         case CH2_MODE:
   1742         case MIXED_MODE:
   1743           break;
   1744         default:
   1745           return (PCMDMX_UNABLE_TO_SET_PARAM);
   1746       }
   1747       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1748       self->userParams.dualChannelMode = (DUAL_CHANNEL_MODE)value;
   1749       self->applyProcessing = ((DUAL_CHANNEL_MODE)value != STEREO_MODE)
   1750                                   ? 1
   1751                                   : 0; /* Force processing if necessary. */
   1752       break;
   1753 
   1754     case DMX_PSEUDO_SURROUND_MODE:
   1755       switch ((PSEUDO_SURROUND_MODE)value) {
   1756         case NEVER_DO_PS_DMX:
   1757         case AUTO_PS_DMX:
   1758         case FORCE_PS_DMX:
   1759           break;
   1760         default:
   1761           return (PCMDMX_UNABLE_TO_SET_PARAM);
   1762       }
   1763       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1764       self->userParams.pseudoSurrMode = (PSEUDO_SURROUND_MODE)value;
   1765       break;
   1766 
   1767     default:
   1768       return (PCMDMX_UNKNOWN_PARAM);
   1769   }
   1770 
   1771   return (PCMDMX_OK);
   1772 }
   1773 
   1774 /** Get one parameter value of one PCM downmix module instance.
   1775  * @param [in] Handle of PCM downmix module instance.
   1776  * @param [in] Parameter to be set.
   1777  * @param [out] Pointer to buffer receiving the parameter value.
   1778  * @returns Returns an error code.
   1779  **/
   1780 PCMDMX_ERROR pcmDmx_GetParam(HANDLE_PCM_DOWNMIX self, const PCMDMX_PARAM param,
   1781                              INT *const pValue) {
   1782   PCM_DMX_USER_PARAMS *pUsrParams;
   1783 
   1784   if ((self == NULL) || (pValue == NULL)) {
   1785     return (PCMDMX_INVALID_HANDLE);
   1786   }
   1787   pUsrParams = &self->userParams;
   1788 
   1789   switch (param) {
   1790     case DMX_PROFILE_SETTING:
   1791       *pValue = (INT)pUsrParams->dmxProfile;
   1792       break;
   1793     case DMX_BS_DATA_EXPIRY_FRAME:
   1794       *pValue = (INT)pUsrParams->expiryFrame;
   1795       break;
   1796     case DMX_BS_DATA_DELAY:
   1797       *pValue = (INT)pUsrParams->frameDelay;
   1798       break;
   1799     case MIN_NUMBER_OF_OUTPUT_CHANNELS:
   1800       *pValue = (INT)pUsrParams->numOutChannelsMin;
   1801       break;
   1802     case MAX_NUMBER_OF_OUTPUT_CHANNELS:
   1803       *pValue = (INT)pUsrParams->numOutChannelsMax;
   1804       break;
   1805     case DMX_DUAL_CHANNEL_MODE:
   1806       *pValue = (INT)pUsrParams->dualChannelMode;
   1807       break;
   1808     case DMX_PSEUDO_SURROUND_MODE:
   1809       *pValue = (INT)pUsrParams->pseudoSurrMode;
   1810       break;
   1811     default:
   1812       return (PCMDMX_UNKNOWN_PARAM);
   1813   }
   1814 
   1815   return (PCMDMX_OK);
   1816 }
   1817 
   1818 /*
   1819  * Read DMX meta-data from a data stream element.
   1820  */
   1821 PCMDMX_ERROR pcmDmx_Parse(HANDLE_PCM_DOWNMIX self, HANDLE_FDK_BITSTREAM hBs,
   1822                           UINT ancDataBits, int isMpeg2) {
   1823   PCMDMX_ERROR errorStatus = PCMDMX_OK;
   1824 
   1825 #define MAX_DSE_ANC_BYTES (16)    /* 15 bytes */
   1826 #define ANC_DATA_SYNC_BYTE (0xBC) /* ancillary data sync byte. */
   1827 
   1828   DMX_BS_META_DATA *pBsMetaData;
   1829 
   1830   int skip4Dmx = 0, skip4Ext = 0;
   1831   int dmxLvlAvail = 0, extDataAvail = 0;
   1832   UINT foundNewData = 0;
   1833   UINT minAncBits = ((isMpeg2) ? 5 : 3) * 8;
   1834 
   1835   if ((self == NULL) || (hBs == NULL)) {
   1836     return (PCMDMX_INVALID_HANDLE);
   1837   }
   1838 
   1839   /* sanity checks */
   1840   if ((ancDataBits < minAncBits) || (ancDataBits > FDKgetValidBits(hBs))) {
   1841     return (PCMDMX_CORRUPT_ANC_DATA);
   1842   }
   1843 
   1844   pBsMetaData = &self->bsMetaData[0];
   1845 
   1846   if (isMpeg2) {
   1847     /* skip DVD ancillary data */
   1848     FDKpushFor(hBs, 16);
   1849   }
   1850 
   1851   /* check sync word */
   1852   if (FDKreadBits(hBs, 8) != ANC_DATA_SYNC_BYTE) {
   1853     return (PCMDMX_CORRUPT_ANC_DATA);
   1854   }
   1855 
   1856   /* skip MPEG audio type and Dolby surround mode */
   1857   FDKpushFor(hBs, 4);
   1858 
   1859   if (isMpeg2) {
   1860     /* int numAncBytes = */ FDKreadBits(hBs, 4);
   1861     /* advanced dynamic range control */
   1862     if (FDKreadBit(hBs)) skip4Dmx += 24;
   1863     /* dialog normalization */
   1864     if (FDKreadBit(hBs)) skip4Dmx += 8;
   1865     /* reproduction_level */
   1866     if (FDKreadBit(hBs)) skip4Dmx += 8;
   1867   } else {
   1868     FDKpushFor(hBs, 2); /* drc presentation mode */
   1869     pBsMetaData->pseudoSurround = (SCHAR)FDKreadBit(hBs);
   1870     FDKpushFor(hBs, 4); /* reserved bits */
   1871   }
   1872 
   1873   /* downmixing levels MPEGx status */
   1874   dmxLvlAvail = FDKreadBit(hBs);
   1875 
   1876   if (isMpeg2) {
   1877     /* scale factor CRC status */
   1878     if (FDKreadBit(hBs)) skip4Ext += 16;
   1879   } else {
   1880     /* ancillary data extension status */
   1881     extDataAvail = FDKreadBit(hBs);
   1882   }
   1883 
   1884   /* audio coding and compression status */
   1885   if (FDKreadBit(hBs)) skip4Ext += 16;
   1886   /* coarse grain timecode status */
   1887   if (FDKreadBit(hBs)) skip4Ext += 16;
   1888   /* fine grain timecode status */
   1889   if (FDKreadBit(hBs)) skip4Ext += 16;
   1890 
   1891   /* skip the useless data to get to the DMX levels */
   1892   FDKpushFor(hBs, skip4Dmx);
   1893 
   1894   /* downmix_levels_MPEGX */
   1895   if (dmxLvlAvail) {
   1896     if (FDKreadBit(hBs)) { /* center_mix_level_on */
   1897       pBsMetaData->cLevIdx = (UCHAR)FDKreadBits(hBs, 3);
   1898       foundNewData |= TYPE_DSE_CLEV_DATA;
   1899     } else {
   1900       FDKreadBits(hBs, 3);
   1901     }
   1902     if (FDKreadBit(hBs)) { /* surround_mix_level_on */
   1903       pBsMetaData->sLevIdx = (UCHAR)FDKreadBits(hBs, 3);
   1904       foundNewData |= TYPE_DSE_SLEV_DATA;
   1905     } else {
   1906       FDKreadBits(hBs, 3);
   1907     }
   1908   }
   1909 
   1910   /* skip the useless data to get to the ancillary data extension */
   1911   FDKpushFor(hBs, skip4Ext);
   1912 
   1913   /* anc data extension (MPEG-4 only) */
   1914   if (extDataAvail) {
   1915     int extDmxLvlSt, extDmxGainSt, extDmxLfeSt;
   1916 
   1917     FDKreadBit(hBs); /* reserved bit */
   1918     extDmxLvlSt = FDKreadBit(hBs);
   1919     extDmxGainSt = FDKreadBit(hBs);
   1920     extDmxLfeSt = FDKreadBit(hBs);
   1921     FDKreadBits(hBs, 4); /* reserved bits */
   1922 
   1923     if (extDmxLvlSt) {
   1924       pBsMetaData->dmixIdxA = (UCHAR)FDKreadBits(hBs, 3);
   1925       pBsMetaData->dmixIdxB = (UCHAR)FDKreadBits(hBs, 3);
   1926       FDKreadBits(hBs, 2); /* reserved bits */
   1927       foundNewData |= TYPE_DSE_DMIX_AB_DATA;
   1928     }
   1929     if (extDmxGainSt) {
   1930       pBsMetaData->dmxGainIdx5 = (UCHAR)FDKreadBits(hBs, 7);
   1931       FDKreadBit(hBs); /* reserved bit */
   1932       pBsMetaData->dmxGainIdx2 = (UCHAR)FDKreadBits(hBs, 7);
   1933       FDKreadBit(hBs); /* reserved bit */
   1934       foundNewData |= TYPE_DSE_DMX_GAIN_DATA;
   1935     }
   1936     if (extDmxLfeSt) {
   1937       pBsMetaData->dmixIdxLfe = (UCHAR)FDKreadBits(hBs, 4);
   1938       FDKreadBits(hBs, 4); /* reserved bits */
   1939       foundNewData |= TYPE_DSE_DMIX_LFE_DATA;
   1940     }
   1941   }
   1942 
   1943   /* final sanity check on the amount of read data */
   1944   if ((INT)FDKgetValidBits(hBs) < 0) {
   1945     errorStatus = PCMDMX_CORRUPT_ANC_DATA;
   1946   }
   1947 
   1948   if ((errorStatus == PCMDMX_OK) && (foundNewData != 0)) {
   1949     /* announce new data */
   1950     pBsMetaData->typeFlags |= foundNewData;
   1951     /* reset expiry counter */
   1952     pBsMetaData->expiryCount = 0;
   1953   }
   1954 
   1955   return (errorStatus);
   1956 }
   1957 
   1958 /*
   1959  * Read DMX meta-data from a data stream element.
   1960  */
   1961 PCMDMX_ERROR pcmDmx_ReadDvbAncData(HANDLE_PCM_DOWNMIX self, UCHAR *pAncDataBuf,
   1962                                    UINT ancDataBytes, int isMpeg2) {
   1963   PCMDMX_ERROR errorStatus = PCMDMX_OK;
   1964   FDK_BITSTREAM bs;
   1965   HANDLE_FDK_BITSTREAM hBs = &bs;
   1966 
   1967   if (self == NULL) {
   1968     return (PCMDMX_INVALID_HANDLE);
   1969   }
   1970 
   1971   /* sanity checks */
   1972   if ((pAncDataBuf == NULL) || (ancDataBytes == 0)) {
   1973     return (PCMDMX_CORRUPT_ANC_DATA);
   1974   }
   1975 
   1976   FDKinitBitStream(hBs, pAncDataBuf, MAX_DSE_ANC_BYTES, ancDataBytes * 8,
   1977                    BS_READER);
   1978 
   1979   errorStatus = pcmDmx_Parse(self, hBs, ancDataBytes * 8, isMpeg2);
   1980 
   1981   return (errorStatus);
   1982 }
   1983 
   1984 /** Set the matrix mixdown information extracted from the PCE of an AAC
   1985  *bitstream. Note: Call only if matrix_mixdown_idx_present is true.
   1986  * @param [in] Handle of PCM downmix module instance.
   1987  * @param [in] The 2 bit matrix mixdown index extracted from PCE.
   1988  * @param [in] The pseudo surround enable flag extracted from PCE.
   1989  * @returns Returns an error code.
   1990  **/
   1991 PCMDMX_ERROR pcmDmx_SetMatrixMixdownFromPce(HANDLE_PCM_DOWNMIX self,
   1992                                             int matrixMixdownPresent,
   1993                                             int matrixMixdownIdx,
   1994                                             int pseudoSurroundEnable) {
   1995   if (self == NULL) {
   1996     return (PCMDMX_INVALID_HANDLE);
   1997   }
   1998 
   1999   {
   2000     DMX_BS_META_DATA *pBsMetaData = &self->bsMetaData[0];
   2001 
   2002     if (matrixMixdownPresent) {
   2003       pBsMetaData->pseudoSurround = (pseudoSurroundEnable) ? 1 : 0;
   2004       pBsMetaData->matrixMixdownIdx = matrixMixdownIdx & 0x03;
   2005       pBsMetaData->typeFlags |= TYPE_PCE_DATA;
   2006       /* Reset expiry counter */
   2007       pBsMetaData->expiryCount = 0;
   2008     }
   2009   }
   2010 
   2011   return (PCMDMX_OK);
   2012 }
   2013 
   2014 /** Apply down or up mixing.
   2015  * @param [in]    Handle of PCM downmix module instance.
   2016  * @param [inout] Pointer to buffer that hold the time domain signal.
   2017  * @param [in]    Pointer where the amount of output samples is returned into.
   2018  * @param [in]    Size of pPcmBuf.
   2019  * @param [inout] Pointer where the amount of output channels is returned into.
   2020  * @param [in]    Input and output samples are processed interleaved.
   2021  * @param [inout] Array where the corresponding channel type for each output
   2022  *audio channel is stored into.
   2023  * @param [inout] Array where the corresponding channel type index for each
   2024  *output audio channel is stored into.
   2025  * @param [in]    Array containing the out channel mapping to be used (From MPEG
   2026  *PCE ordering to whatever is required).
   2027  * @param [out]   Pointer on a field receiving the scale factor that has to be
   2028  *applied on all samples afterwards. If the handed pointer is NULL scaling is
   2029  *done internally.
   2030  * @returns Returns an error code.
   2031  **/
   2032 PCMDMX_ERROR pcmDmx_ApplyFrame(HANDLE_PCM_DOWNMIX self, DMX_PCM *pPcmBuf,
   2033                                const int pcmBufSize, UINT frameSize,
   2034                                INT *nChannels, INT fInterleaved,
   2035                                AUDIO_CHANNEL_TYPE channelType[],
   2036                                UCHAR channelIndices[],
   2037                                const FDK_channelMapDescr *const mapDescr,
   2038                                INT *pDmxOutScale) {
   2039   PCM_DMX_USER_PARAMS *pParam = NULL;
   2040   PCMDMX_ERROR errorStatus = PCMDMX_OK;
   2041   DUAL_CHANNEL_MODE dualChannelMode;
   2042   PCM_DMX_CHANNEL_MODE inChMode;
   2043   PCM_DMX_CHANNEL_MODE outChMode;
   2044   INT devNull; /* Just a dummy to avoid a lot of branches in the code */
   2045   int numOutChannels, numInChannels;
   2046   int inStride, outStride, offset;
   2047   int dmxMaxScale, dmxScale;
   2048   int slot;
   2049   UCHAR inOffsetTable[(8)];
   2050 
   2051   DMX_BS_META_DATA bsMetaData;
   2052 
   2053   if ((self == NULL) || (nChannels == NULL) || (channelType == NULL) ||
   2054       (channelIndices == NULL) || (!FDK_chMapDescr_isValid(mapDescr))) {
   2055     return (PCMDMX_INVALID_HANDLE);
   2056   }
   2057 
   2058   /* Init the output scaling */
   2059   dmxScale = 0;
   2060   if (pDmxOutScale != NULL) {
   2061     /* Avoid final scaling internally and hand it to the outside world. */
   2062     *pDmxOutScale = 0;
   2063     dmxMaxScale = (3);
   2064   } else {
   2065     /* Apply the scaling internally. */
   2066     pDmxOutScale = &devNull; /* redirect to temporal stack memory */
   2067     dmxMaxScale = 0;
   2068   }
   2069 
   2070   pParam = &self->userParams;
   2071   numInChannels = *nChannels;
   2072 
   2073   /* Perform some input sanity checks */
   2074   if (pPcmBuf == NULL) {
   2075     return (PCMDMX_INVALID_ARGUMENT);
   2076   }
   2077   if (frameSize == 0) {
   2078     return (PCMDMX_INVALID_ARGUMENT);
   2079   }
   2080   if (numInChannels == 0) {
   2081     return (PCMDMX_INVALID_ARGUMENT);
   2082   }
   2083   if (numInChannels > (8)) {
   2084     return (PCMDMX_INVALID_CH_CONFIG);
   2085   }
   2086 
   2087   /* Check on misconfiguration */
   2088   FDK_ASSERT((pParam->numOutChannelsMax <= 0) ||
   2089              (pParam->numOutChannelsMax >= pParam->numOutChannelsMin));
   2090 
   2091   /* Determine if the module has to do processing */
   2092   if ((self->applyProcessing == 0) &&
   2093       ((pParam->numOutChannelsMax <= 0) ||
   2094        (pParam->numOutChannelsMax >= numInChannels)) &&
   2095       (pParam->numOutChannelsMin <= numInChannels)) {
   2096     /* Nothing to do */
   2097     return (errorStatus);
   2098   }
   2099 
   2100   /* Determine the number of output channels */
   2101   if ((pParam->numOutChannelsMax > 0) &&
   2102       (numInChannels > pParam->numOutChannelsMax)) {
   2103     numOutChannels = pParam->numOutChannelsMax;
   2104   } else if (numInChannels < pParam->numOutChannelsMin) {
   2105     numOutChannels = pParam->numOutChannelsMin;
   2106   } else {
   2107     numOutChannels = numInChannels;
   2108   }
   2109 
   2110   /* Check I/O buffer size */
   2111   if ((UINT)pcmBufSize < (UINT)numOutChannels * frameSize) {
   2112     return (PCMDMX_OUTPUT_BUFFER_TOO_SMALL);
   2113   }
   2114 
   2115   dualChannelMode = pParam->dualChannelMode;
   2116 
   2117   /* Analyse input channel configuration and get channel offset
   2118    * table that can be accessed with the fixed channel labels. */
   2119   errorStatus = getChannelMode(numInChannels, channelType, channelIndices,
   2120                                inOffsetTable, &inChMode);
   2121   if (PCMDMX_IS_FATAL_ERROR(errorStatus) || (inChMode == CH_MODE_UNDEFINED)) {
   2122     /* We don't need to restore because the channel
   2123        configuration has not been changed. Just exit. */
   2124     return (PCMDMX_INVALID_CH_CONFIG);
   2125   }
   2126 
   2127   /* Set input stride and offset */
   2128   if (fInterleaved) {
   2129     inStride = numInChannels;
   2130     offset = 1; /* Channel specific offset factor */
   2131   } else {
   2132     inStride = 1;
   2133     offset = frameSize; /* Channel specific offset factor */
   2134   }
   2135 
   2136   /* Reset downmix meta data if necessary */
   2137   if ((pParam->expiryFrame > 0) &&
   2138       (++self->bsMetaData[0].expiryCount >
   2139        pParam
   2140            ->expiryFrame)) { /* The metadata read from bitstream is too old. */
   2141 #ifdef FDK_ASSERT_ENABLE
   2142     PCMDMX_ERROR err = pcmDmx_Reset(self, PCMDMX_RESET_BS_DATA);
   2143     FDK_ASSERT(err == PCMDMX_OK);
   2144 #else
   2145     pcmDmx_Reset(self, PCMDMX_RESET_BS_DATA);
   2146 #endif
   2147   }
   2148   FDKmemcpy(&bsMetaData, &self->bsMetaData[pParam->frameDelay],
   2149             sizeof(DMX_BS_META_DATA));
   2150   /* Maintain delay line */
   2151   for (slot = pParam->frameDelay; slot > 0; slot -= 1) {
   2152     FDKmemcpy(&self->bsMetaData[slot], &self->bsMetaData[slot - 1],
   2153               sizeof(DMX_BS_META_DATA));
   2154   }
   2155 
   2156   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2157    * - - - - - - - - - - - - - - - - - - */
   2158   if (numInChannels > numOutChannels) { /* Apply downmix */
   2159     DMX_PCM *pInPcm[(8)] = {NULL};
   2160     DMX_PCM *pOutPcm[(8)] = {NULL};
   2161     FIXP_DMX mixFactors[(8)][(8)];
   2162     UCHAR outOffsetTable[(8)];
   2163     UINT sample;
   2164     int chCfg = 0;
   2165     int bypScale = 0;
   2166 
   2167     if (numInChannels > SIX_CHANNEL) {
   2168       AUDIO_CHANNEL_TYPE multiPurposeChType[2];
   2169 
   2170       /* Get the type of the multipurpose channels */
   2171       multiPurposeChType[0] =
   2172           channelType[inOffsetTable[LEFT_MULTIPRPS_CHANNEL]];
   2173       multiPurposeChType[1] =
   2174           channelType[inOffsetTable[RIGHT_MULTIPRPS_CHANNEL]];
   2175 
   2176       /* Check if the input configuration is one defined in the standard. */
   2177       switch (inChMode) {
   2178         case CH_MODE_5_0_2_1: /* chCfg 7 || 14 */
   2179           /* Further analyse the input config to distinguish the two
   2180            * CH_MODE_5_0_2_1 configs. */
   2181           if ((multiPurposeChType[0] == ACT_FRONT_TOP) &&
   2182               (multiPurposeChType[1] == ACT_FRONT_TOP)) {
   2183             chCfg = 14;
   2184           } else {
   2185             chCfg = 7;
   2186           }
   2187           break;
   2188         case CH_MODE_3_0_3_1: /* chCfg 11 */
   2189           chCfg = 11;
   2190           break;
   2191         case CH_MODE_3_0_4_1: /* chCfg 12 */
   2192           chCfg = 12;
   2193           break;
   2194         default:
   2195           chCfg = 0; /* Not a known config */
   2196           break;
   2197       }
   2198     }
   2199 
   2200     /* Set this stages output stride and channel mode: */
   2201     outStride = (fInterleaved) ? numOutChannels : 1;
   2202     outChMode = outChModeTable[numOutChannels];
   2203     FDK_ASSERT(outChMode != CH_MODE_UNDEFINED);
   2204 
   2205     /* Get channel description and channel mapping for the desired output
   2206      * configuration. */
   2207     getChannelDescription(outChMode, mapDescr, channelType, channelIndices,
   2208                           outOffsetTable);
   2209     /* Now there is no way back because we modified the channel configuration!
   2210      */
   2211 
   2212     /* Create the DMX matrix */
   2213     errorStatus =
   2214         getMixFactors((chCfg > 0) ? 1 : 0,
   2215                       (chCfg > 0) ? (PCM_DMX_CHANNEL_MODE)chCfg : inChMode,
   2216                       outChMode, pParam, &bsMetaData, mixFactors, &dmxScale);
   2217     /* No fatal errors can occur here. The function is designed to always return
   2218        a valid matrix. The error code is used to signal configurations and
   2219        matrices that are not conform to any standard. */
   2220 
   2221     /* Determine the final scaling */
   2222     bypScale = fMin(dmxMaxScale, dmxScale);
   2223     *pDmxOutScale += bypScale;
   2224     dmxScale -= bypScale;
   2225 
   2226     { /* Set channel pointer for input. Remove empty cols. */
   2227       int inCh, outCh, map[(8)];
   2228       int ch = 0;
   2229       for (inCh = 0; inCh < (8); inCh += 1) {
   2230         if (inOffsetTable[inCh] < (UCHAR)numInChannels) {
   2231           pInPcm[ch] = &pPcmBuf[inOffsetTable[inCh] * offset];
   2232           map[ch++] = inCh;
   2233         }
   2234       }
   2235       for (; ch < (8); ch += 1) {
   2236         map[ch] = ch;
   2237       }
   2238 
   2239       /* Remove unused cols from factor matrix */
   2240       for (inCh = 0; inCh < numInChannels; inCh += 1) {
   2241         if (inCh != map[inCh]) {
   2242           for (outCh = 0; outCh < (8); outCh += 1) {
   2243             mixFactors[outCh][inCh] = mixFactors[outCh][map[inCh]];
   2244           }
   2245         }
   2246       }
   2247 
   2248       /* Set channel pointer for output. Remove empty cols. */
   2249       ch = 0;
   2250       for (outCh = 0; outCh < (8); outCh += 1) {
   2251         if (outOffsetTable[outCh] < (UCHAR)numOutChannels) {
   2252           pOutPcm[ch] = &pPcmBuf[outOffsetTable[outCh] * offset];
   2253           map[ch++] = outCh;
   2254         }
   2255       }
   2256       for (; ch < (8); ch += 1) {
   2257         map[ch] = ch;
   2258       }
   2259 
   2260       /* Remove unused rows from factor matrix */
   2261       for (outCh = 0; outCh < numOutChannels; outCh += 1) {
   2262         if (outCh != map[outCh]) {
   2263           FDKmemcpy(&mixFactors[outCh], &mixFactors[map[outCh]],
   2264                     (8) * sizeof(FIXP_DMX));
   2265         }
   2266       }
   2267     }
   2268 
   2269     /* Sample processing loop */
   2270     for (sample = 0; sample < frameSize; sample++) {
   2271       DMX_PCM tIn[(8)] = {0};
   2272       FIXP_DBL tOut[(8)] = {(FIXP_DBL)0};
   2273       int inCh, outCh;
   2274 
   2275       /* Preload all input samples */
   2276       for (inCh = 0; inCh < numInChannels; inCh += 1) {
   2277         if (pInPcm[inCh] != NULL) {
   2278           tIn[inCh] = *pInPcm[inCh];
   2279           pInPcm[inCh] += inStride;
   2280         } else {
   2281           tIn[inCh] = (DMX_PCM)0;
   2282         }
   2283       }
   2284       /* Apply downmix coefficients to input samples and accumulate for output
   2285        */
   2286       for (outCh = 0; outCh < numOutChannels; outCh += 1) {
   2287         for (inCh = 0; inCh < numInChannels; inCh += 1) {
   2288           tOut[outCh] += fMult((DMX_PCMF)tIn[inCh], mixFactors[outCh][inCh]);
   2289         }
   2290         FDK_ASSERT(pOutPcm[outCh] >= pPcmBuf);
   2291         FDK_ASSERT(pOutPcm[outCh] < &pPcmBuf[pcmBufSize]);
   2292         /* Write sample */
   2293         *pOutPcm[outCh] = (DMX_PCM)SATURATE_SHIFT(
   2294             tOut[outCh], DFRACT_BITS - DMX_PCM_BITS - dmxScale, DMX_PCM_BITS);
   2295         pOutPcm[outCh] += outStride;
   2296       }
   2297     }
   2298 
   2299     /* Update the number of output channels */
   2300     *nChannels = numOutChannels;
   2301 
   2302   } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2303        - - - - - - - - - - - - - - - - - - */
   2304   else if (numInChannels < numOutChannels) { /* Apply rudimentary upmix */
   2305     /* Set up channel pointer */
   2306     UCHAR outOffsetTable[(8)];
   2307 
   2308     /* FIRST STAGE
   2309          Create a stereo/dual channel signal */
   2310     if (numInChannels == ONE_CHANNEL) {
   2311       DMX_PCM *pInPcm[(8)];
   2312       DMX_PCM *pOutLF, *pOutRF;
   2313       UINT sample;
   2314 
   2315       /* Set this stages output stride and channel mode: */
   2316       outStride = (fInterleaved) ? TWO_CHANNEL : 1;
   2317       outChMode = outChModeTable[TWO_CHANNEL];
   2318 
   2319       /* Get channel description and channel mapping for this
   2320        * stages number of output channels (always STEREO). */
   2321       getChannelDescription(outChMode, mapDescr, channelType, channelIndices,
   2322                             outOffsetTable);
   2323       /* Now there is no way back because we modified the channel configuration!
   2324        */
   2325 
   2326       /* Set input channel pointer. The first channel is always at index 0. */
   2327       pInPcm[CENTER_FRONT_CHANNEL] =
   2328           &pPcmBuf[(frameSize - 1) *
   2329                    inStride]; /* Considering input mapping could lead to a
   2330                                  invalid pointer here if the channel is not
   2331                                  declared to be a front channel. */
   2332 
   2333       /* Set output channel pointer (for this stage). */
   2334       pOutLF = &pPcmBuf[outOffsetTable[LEFT_FRONT_CHANNEL] * offset +
   2335                         (frameSize - 1) * outStride];
   2336       pOutRF = &pPcmBuf[outOffsetTable[RIGHT_FRONT_CHANNEL] * offset +
   2337                         (frameSize - 1) * outStride];
   2338 
   2339       /* 1/0 input: */
   2340       for (sample = 0; sample < frameSize; sample++) {
   2341         /* L' = C;  R' = C; */
   2342         *pOutLF = *pOutRF = *pInPcm[CENTER_FRONT_CHANNEL];
   2343 
   2344         pInPcm[CENTER_FRONT_CHANNEL] -= inStride;
   2345         pOutLF -= outStride;
   2346         pOutRF -= outStride;
   2347       }
   2348 
   2349       /* Prepare for next stage: */
   2350       inStride = outStride;
   2351       inChMode = outChMode;
   2352       FDKmemcpy(inOffsetTable, outOffsetTable, (8) * sizeof(UCHAR));
   2353     }
   2354 
   2355     /* SECOND STAGE
   2356          Extend with zero channels to achieved the desired number of output
   2357        channels. */
   2358     if (numOutChannels > TWO_CHANNEL) {
   2359       DMX_PCM *pIn[(8)] = {NULL};
   2360       DMX_PCM *pOut[(8)] = {NULL};
   2361       UINT sample;
   2362       AUDIO_CHANNEL_TYPE inChTypes[(8)];
   2363       UCHAR inChIndices[(8)];
   2364       UCHAR numChPerGrp[2][(4)];
   2365       int nContentCh = 0; /* Number of channels with content */
   2366       int nEmptyCh = 0;   /* Number of channels with content */
   2367       int ch, chGrp, isCompatible = 1;
   2368 
   2369       /* Do not change the signalling which is the channel types and indices.
   2370          Just reorder and add channels. So first save the input signalling. */
   2371       FDKmemcpy(inChTypes, channelType,
   2372                 numInChannels * sizeof(AUDIO_CHANNEL_TYPE));
   2373       FDKmemclear(inChTypes + numInChannels,
   2374                   ((8) - numInChannels) * sizeof(AUDIO_CHANNEL_TYPE));
   2375       FDKmemcpy(inChIndices, channelIndices, numInChannels * sizeof(UCHAR));
   2376       FDKmemclear(inChIndices + numInChannels,
   2377                   ((8) - numInChannels) * sizeof(UCHAR));
   2378 
   2379       /* Set this stages output stride and channel mode: */
   2380       outStride = (fInterleaved) ? numOutChannels : 1;
   2381       outChMode = outChModeTable[numOutChannels];
   2382       FDK_ASSERT(outChMode != CH_MODE_UNDEFINED);
   2383 
   2384       /* Check if input channel config can be easily mapped to the desired
   2385        * output config. */
   2386       for (chGrp = 0; chGrp < (4); chGrp += 1) {
   2387         numChPerGrp[IN][chGrp] = (inChMode >> (chGrp * 4)) & 0xF;
   2388         numChPerGrp[OUT][chGrp] = (outChMode >> (chGrp * 4)) & 0xF;
   2389 
   2390         if (numChPerGrp[IN][chGrp] > numChPerGrp[OUT][chGrp]) {
   2391           isCompatible = 0;
   2392           break;
   2393         }
   2394       }
   2395 
   2396       if (isCompatible) {
   2397         /* Get new channel description and channel
   2398          * mapping for the desired output channel mode. */
   2399         getChannelDescription(outChMode, mapDescr, channelType, channelIndices,
   2400                               outOffsetTable);
   2401         /* If the input config has a back center channel but the output
   2402            config has not, copy it to left and right (if available). */
   2403         if ((numChPerGrp[IN][CH_GROUP_REAR] % 2) &&
   2404             !(numChPerGrp[OUT][CH_GROUP_REAR] % 2)) {
   2405           if (numChPerGrp[IN][CH_GROUP_REAR] == 1) {
   2406             inOffsetTable[RIGHT_REAR_CHANNEL] =
   2407                 inOffsetTable[LEFT_REAR_CHANNEL];
   2408           } else if (numChPerGrp[IN][CH_GROUP_REAR] == 3) {
   2409             inOffsetTable[RIGHT_MULTIPRPS_CHANNEL] =
   2410                 inOffsetTable[LEFT_MULTIPRPS_CHANNEL];
   2411           }
   2412         }
   2413       } else {
   2414         /* Just copy and extend the original config */
   2415         FDKmemcpy(outOffsetTable, inOffsetTable, (8) * sizeof(UCHAR));
   2416       }
   2417 
   2418       /* Set I/O channel pointer.
   2419          Note: The following assignment algorithm clears the channel offset
   2420          tables. Thus they can not be used afterwards. */
   2421       for (ch = 0; ch < (8); ch += 1) {
   2422         if ((outOffsetTable[ch] < 255) &&
   2423             (inOffsetTable[ch] < 255)) { /* Set I/O pointer: */
   2424           pIn[nContentCh] =
   2425               &pPcmBuf[inOffsetTable[ch] * offset + (frameSize - 1) * inStride];
   2426           pOut[nContentCh] = &pPcmBuf[outOffsetTable[ch] * offset +
   2427                                       (frameSize - 1) * outStride];
   2428           /* Update signalling */
   2429           channelType[outOffsetTable[ch]] = inChTypes[inOffsetTable[ch]];
   2430           channelIndices[outOffsetTable[ch]] = inChIndices[inOffsetTable[ch]];
   2431           inOffsetTable[ch] = 255;
   2432           outOffsetTable[ch] = 255;
   2433           nContentCh += 1;
   2434         }
   2435       }
   2436       if (isCompatible) {
   2437         /* Assign the remaining input channels.
   2438            This is just a safety appliance. We should never need it. */
   2439         for (ch = 0; ch < (8); ch += 1) {
   2440           if (inOffsetTable[ch] < 255) {
   2441             int outCh;
   2442             for (outCh = 0; outCh < (8); outCh += 1) {
   2443               if (outOffsetTable[outCh] < 255) {
   2444                 break;
   2445               }
   2446             }
   2447             if (outCh >= (8)) {
   2448               FDK_ASSERT(0);
   2449               break;
   2450             }
   2451             /* Set I/O pointer: */
   2452             pIn[nContentCh] = &pPcmBuf[inOffsetTable[ch] * offset +
   2453                                        (frameSize - 1) * inStride];
   2454             pOut[nContentCh] = &pPcmBuf[outOffsetTable[outCh] * offset +
   2455                                         (frameSize - 1) * outStride];
   2456             /* Update signalling */
   2457             FDK_ASSERT(inOffsetTable[outCh] < numInChannels);
   2458             FDK_ASSERT(outOffsetTable[outCh] < numOutChannels);
   2459             channelType[outOffsetTable[outCh]] = inChTypes[inOffsetTable[ch]];
   2460             channelIndices[outOffsetTable[outCh]] =
   2461                 inChIndices[inOffsetTable[ch]];
   2462             inOffsetTable[ch] = 255;
   2463             outOffsetTable[outCh] = 255;
   2464             nContentCh += 1;
   2465           }
   2466         }
   2467         /* Set the remaining output channel pointer */
   2468         for (ch = 0; ch < (8); ch += 1) {
   2469           if (outOffsetTable[ch] < 255) {
   2470             pOut[nContentCh + nEmptyCh] = &pPcmBuf[outOffsetTable[ch] * offset +
   2471                                                    (frameSize - 1) * outStride];
   2472             /* Expand output signalling */
   2473             channelType[outOffsetTable[ch]] = ACT_NONE;
   2474             channelIndices[outOffsetTable[ch]] = (UCHAR)nEmptyCh;
   2475             outOffsetTable[ch] = 255;
   2476             nEmptyCh += 1;
   2477           }
   2478         }
   2479       } else {
   2480         /* Set the remaining output channel pointer */
   2481         for (ch = nContentCh; ch < numOutChannels; ch += 1) {
   2482           pOut[ch] = &pPcmBuf[ch * offset + (frameSize - 1) * outStride];
   2483           /* Expand output signalling */
   2484           channelType[ch] = ACT_NONE;
   2485           channelIndices[ch] = (UCHAR)nEmptyCh;
   2486           nEmptyCh += 1;
   2487         }
   2488       }
   2489 
   2490       /* First copy the channels that have signal */
   2491       for (sample = 0; sample < frameSize; sample += 1) {
   2492         DMX_PCM tIn[(8)];
   2493         /* Read all channel samples */
   2494         for (ch = 0; ch < nContentCh; ch += 1) {
   2495           tIn[ch] = *pIn[ch];
   2496           pIn[ch] -= inStride;
   2497         }
   2498         /* Write all channel samples */
   2499         for (ch = 0; ch < nContentCh; ch += 1) {
   2500           *pOut[ch] = tIn[ch];
   2501           pOut[ch] -= outStride;
   2502         }
   2503       }
   2504 
   2505       /* Clear all the other channels */
   2506       for (sample = 0; sample < frameSize; sample++) {
   2507         for (ch = nContentCh; ch < numOutChannels; ch += 1) {
   2508           *pOut[ch] = (DMX_PCM)0;
   2509           pOut[ch] -= outStride;
   2510         }
   2511       }
   2512     }
   2513 
   2514     /* update the number of output channels */
   2515     *nChannels = numOutChannels;
   2516   } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2517        - - - - - - - - - - - - - - - - - - */
   2518   else if (numInChannels == numOutChannels) {
   2519     /* Don't need to change the channel description here */
   2520 
   2521     switch (numInChannels) {
   2522       case 2: { /* Set up channel pointer */
   2523         DMX_PCM *pInPcm[(8)];
   2524         DMX_PCM *pOutL, *pOutR;
   2525         FIXP_DMX flev;
   2526 
   2527         UINT sample;
   2528 
   2529         if (fInterleaved) {
   2530           inStride = numInChannels;
   2531           outStride =
   2532               2; /* fixed !!! (below stereo is donwmixed to mono if required */
   2533           offset = 1; /* Channel specific offset factor */
   2534         } else {
   2535           inStride = 1;
   2536           outStride = 1;
   2537           offset = frameSize; /* Channel specific offset factor */
   2538         }
   2539 
   2540         /* Set input channel pointer */
   2541         pInPcm[LEFT_FRONT_CHANNEL] =
   2542             &pPcmBuf[inOffsetTable[LEFT_FRONT_CHANNEL] * offset];
   2543         pInPcm[RIGHT_FRONT_CHANNEL] =
   2544             &pPcmBuf[inOffsetTable[RIGHT_FRONT_CHANNEL] * offset];
   2545 
   2546         /* Set output channel pointer (same as input) */
   2547         pOutL = pInPcm[LEFT_FRONT_CHANNEL];
   2548         pOutR = pInPcm[RIGHT_FRONT_CHANNEL];
   2549 
   2550         /* Set downmix levels: */
   2551         flev = FL2FXCONST_DMX(0.70710678f);
   2552         /* 2/0 input: */
   2553         switch (dualChannelMode) {
   2554           case CH1_MODE: /* L' = 0.707 * Ch1;  R' = 0.707 * Ch1 */
   2555             for (sample = 0; sample < frameSize; sample++) {
   2556               *pOutL = *pOutR = (DMX_PCM)SATURATE_RIGHT_SHIFT(
   2557                   fMult((DMX_PCMF)*pInPcm[LEFT_FRONT_CHANNEL], flev),
   2558                   DFRACT_BITS - DMX_PCM_BITS, DMX_PCM_BITS);
   2559 
   2560               pInPcm[LEFT_FRONT_CHANNEL] += inStride;
   2561               pOutL += outStride;
   2562               pOutR += outStride;
   2563             }
   2564             break;
   2565           case CH2_MODE: /* L' = 0.707 * Ch2;  R' = 0.707 * Ch2 */
   2566             for (sample = 0; sample < frameSize; sample++) {
   2567               *pOutL = *pOutR = (DMX_PCM)SATURATE_RIGHT_SHIFT(
   2568                   fMult((DMX_PCMF)*pInPcm[RIGHT_FRONT_CHANNEL], flev),
   2569                   DFRACT_BITS - DMX_PCM_BITS, DMX_PCM_BITS);
   2570 
   2571               pInPcm[RIGHT_FRONT_CHANNEL] += inStride;
   2572               pOutL += outStride;
   2573               pOutR += outStride;
   2574             }
   2575             break;
   2576           case MIXED_MODE: /* L' = 0.5*Ch1 + 0.5*Ch2;  R' = 0.5*Ch1 + 0.5*Ch2 */
   2577             for (sample = 0; sample < frameSize; sample++) {
   2578               *pOutL = *pOutR = (*pInPcm[LEFT_FRONT_CHANNEL] >> 1) +
   2579                                 (*pInPcm[RIGHT_FRONT_CHANNEL] >> 1);
   2580 
   2581               pInPcm[LEFT_FRONT_CHANNEL] += inStride;
   2582               pInPcm[RIGHT_FRONT_CHANNEL] += inStride;
   2583               pOutL += outStride;
   2584               pOutR += outStride;
   2585             }
   2586             break;
   2587           default:
   2588           case STEREO_MODE:
   2589             /* nothing to do */
   2590             break;
   2591         }
   2592       } break;
   2593 
   2594       default:
   2595         /* nothing to do */
   2596         break;
   2597     }
   2598   }
   2599 
   2600   return (errorStatus);
   2601 }
   2602 
   2603 /** Close an instance of the PCM downmix module.
   2604  * @param [inout] Pointer to a buffer containing the handle of the instance.
   2605  * @returns Returns an error code.
   2606  **/
   2607 PCMDMX_ERROR pcmDmx_Close(HANDLE_PCM_DOWNMIX *pSelf) {
   2608   if (pSelf == NULL) {
   2609     return (PCMDMX_INVALID_HANDLE);
   2610   }
   2611 
   2612   FreePcmDmxInstance(pSelf);
   2613   *pSelf = NULL;
   2614 
   2615   return (PCMDMX_OK);
   2616 }
   2617 
   2618 /** Get library info for this module.
   2619  * @param [out] Pointer to an allocated LIB_INFO structure.
   2620  * @returns Returns an error code.
   2621  */
   2622 PCMDMX_ERROR pcmDmx_GetLibInfo(LIB_INFO *info) {
   2623   int i;
   2624 
   2625   if (info == NULL) {
   2626     return PCMDMX_INVALID_ARGUMENT;
   2627   }
   2628 
   2629   /* Search for next free tab */
   2630   for (i = 0; i < FDK_MODULE_LAST; i++) {
   2631     if (info[i].module_id == FDK_NONE) break;
   2632   }
   2633   if (i == FDK_MODULE_LAST) {
   2634     return PCMDMX_INVALID_ARGUMENT;
   2635   }
   2636 
   2637   /* Add the library info */
   2638   info[i].module_id = FDK_PCMDMX;
   2639   info[i].version =
   2640       LIB_VERSION(PCMUTIL_LIB_VL0, PCMUTIL_LIB_VL1, PCMUTIL_LIB_VL2);
   2641   LIB_VERSION_STRING(info + i);
   2642   info[i].build_date = PCMUTIL_LIB_BUILD_DATE;
   2643   info[i].build_time = PCMUTIL_LIB_BUILD_TIME;
   2644   info[i].title = PCMDMX_LIB_TITLE;
   2645 
   2646   /* Set flags */
   2647   info[i].flags = 0 | CAPF_DMX_BLIND /* At least blind downmixing is possible */
   2648                   | CAPF_DMX_PCE     /* Guided downmix with data from MPEG-2/4
   2649                                         Program Config Elements (PCE). */
   2650                   | CAPF_DMX_ARIB /* PCE guided downmix with slightly different
   2651                                      equations and levels. */
   2652                   | CAPF_DMX_DVB  /* Guided downmix with data from DVB ancillary
   2653                                      data fields. */
   2654                   | CAPF_DMX_CH_EXP /* Simple upmixing by dublicating channels
   2655                                        or adding zero channels. */
   2656                   | CAPF_DMX_6_CH | CAPF_DMX_8_CH;
   2657 
   2658   /* Add lib info for FDK tools (if not yet done). */
   2659   FDK_toolsGetLibInfo(info);
   2660 
   2661   return PCMDMX_OK;
   2662 }
   2663