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       case CH_MODE_3_0_3_1: /* chCfg 11 */
   1047         /* 6.1ch:  C' = C;  L' = L;  R' = R;  LFE' = LFE;
   1048                    Ls' = Ls*dmix_a_idx + Cs*dmix_b_idx;
   1049                    Rs' = Rs*dmix_a_idx + Cs*dmix_b_idx; */
   1050         dmxClearChannel(
   1051             mixFactors, mixScales,
   1052             RIGHT_MULTIPRPS_CHANNEL); /* clear empty input channel */
   1053         dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL,
   1054                       LEFT_REAR_CHANNEL, dMixFactA, dMixScaleA);
   1055         dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL,
   1056                       LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1057         dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1058                       RIGHT_REAR_CHANNEL, dMixFactA, dMixScaleA);
   1059         dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1060                       LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1061         break;
   1062       case CH_MODE_3_0_4_1: /* chCfg 12 */
   1063         /* 7.1ch Surround Back:  C' = C;  L' = L;  R' = R;  LFE' = LFE;
   1064                                  Ls' = Ls*dmix_a_idx + Lsr*dmix_b_idx;
   1065                                  Rs' = Rs*dmix_a_idx + Rsr*dmix_b_idx; */
   1066         dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL,
   1067                       LEFT_REAR_CHANNEL, dMixFactA, dMixScaleA);
   1068         dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL,
   1069                       LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1070         dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1071                       RIGHT_REAR_CHANNEL, dMixFactA, dMixScaleA);
   1072         dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1073                       RIGHT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1074         break;
   1075       case CH_MODE_5_0_1_0:
   1076       case CH_MODE_5_0_1_1:
   1077         dmxClearChannel(mixFactors, mixScales,
   1078                         RIGHT_REAR_CHANNEL); /* clear empty input channel */
   1079         dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1080                       LEFT_REAR_CHANNEL, FL2FXCONST_DMX(0.5f), 1);
   1081         dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL,
   1082                       LEFT_REAR_CHANNEL, FL2FXCONST_DMX(0.5f), 1);
   1083       case CH_MODE_5_2_1_0:
   1084         isValidCfg = FALSE;
   1085         err = PCMDMX_INVALID_MODE;
   1086       case CH_MODE_5_0_2_1: /* chCfg 7 || 14 */
   1087         if (inChCfg == 14) {
   1088           /* 7.1ch Front Height:  C' = C;  Ls' = Ls;  Rs' = Rs;  LFE' = LFE;
   1089                                   L' = L*dmix_a_idx + Lv*dmix_b_idx;
   1090                                   R' = R*dmix_a_idx + Rv*dmix_b_idx; */
   1091           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1092                         LEFT_FRONT_CHANNEL, dMixFactA, dMixScaleA);
   1093           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1094                         LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1095           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1096                         RIGHT_FRONT_CHANNEL, dMixFactA, dMixScaleA);
   1097           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1098                         RIGHT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1099         } else {
   1100           /* 7.1ch Front:  Ls' = Ls;  Rs' = Rs;  LFE' = LFE;
   1101                            C' = C + (Lc+Rc)*dmix_a_idx;
   1102                            L' = L + Lc*dmix_b_idx;
   1103                            R' = R + Rc*dmix_b_idx; */
   1104           dmxSetChannel(mixFactors, mixScales, CENTER_FRONT_CHANNEL,
   1105                         LEFT_MULTIPRPS_CHANNEL, dMixFactA, dMixScaleA);
   1106           dmxSetChannel(mixFactors, mixScales, CENTER_FRONT_CHANNEL,
   1107                         RIGHT_MULTIPRPS_CHANNEL, dMixFactA, dMixScaleA);
   1108           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1109                         LEFT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1110           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1111                         LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 1);
   1112           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1113                         RIGHT_MULTIPRPS_CHANNEL, dMixFactB, dMixScaleB);
   1114           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1115                         RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 1);
   1116         }
   1117         break;
   1118       default:
   1119         /* Nothing to do. Just use the identity matrix. */
   1120         isValidCfg = FALSE;
   1121         err = PCMDMX_INVALID_MODE;
   1122         break;
   1123     }
   1124 
   1125     /* Add additional DMX gain */
   1126     if ((isValidCfg == TRUE) &&
   1127         (pMetaData->dmxGainIdx5 != 0)) { /* Apply DMX gain 5 */
   1128       FIXP_DMX dmxGain;
   1129       INT dmxScale;
   1130       INT sign = (pMetaData->dmxGainIdx5 & 0x40) ? -1 : 1;
   1131       INT val = pMetaData->dmxGainIdx5 & 0x3F;
   1132 
   1133       /* 10^(dmx_gain_5/80) */
   1134       dmxGain = FX_DBL2FX_DMX(
   1135           fLdPow(FL2FXCONST_DBL(0.830482023721841f), 2, /* log2(10) */
   1136                  (FIXP_DBL)(sign * val * (LONG)FL2FXCONST_DBL(0.0125f)), 0,
   1137                  &dmxScale));
   1138       /* Currently only positive scale factors supported! */
   1139       if (dmxScale < 0) {
   1140         dmxGain >>= -dmxScale;
   1141         dmxScale = 0;
   1142       }
   1143 
   1144       dmxSetChannel(mixFactors, mixScales, CENTER_FRONT_CHANNEL,
   1145                     CENTER_FRONT_CHANNEL, dmxGain, dmxScale);
   1146       dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1147                     LEFT_FRONT_CHANNEL, dmxGain, dmxScale);
   1148       dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1149                     RIGHT_FRONT_CHANNEL, dmxGain, dmxScale);
   1150       dmxSetChannel(mixFactors, mixScales, LEFT_REAR_CHANNEL, LEFT_REAR_CHANNEL,
   1151                     dmxGain, dmxScale);
   1152       dmxSetChannel(mixFactors, mixScales, RIGHT_REAR_CHANNEL,
   1153                     RIGHT_REAR_CHANNEL, dmxGain, dmxScale);
   1154       dmxSetChannel(mixFactors, mixScales, LOW_FREQUENCY_CHANNEL,
   1155                     LOW_FREQUENCY_CHANNEL, dmxGain, dmxScale);
   1156     }
   1157 
   1158     /* Mark the output channels */
   1159     valid[CENTER_FRONT_CHANNEL] = 1;
   1160     valid[LEFT_FRONT_CHANNEL] = 1;
   1161     valid[RIGHT_FRONT_CHANNEL] = 1;
   1162     valid[LEFT_REAR_CHANNEL] = 1;
   1163     valid[RIGHT_REAR_CHANNEL] = 1;
   1164     valid[LOW_FREQUENCY_CHANNEL] = 1;
   1165 
   1166     /* Update channel mode for the next stage */
   1167     inChMode = CH_MODE_3_0_2_1;
   1168   }
   1169 
   1170     /* For the X (> 6) to 6 channel downmix we had no choice.
   1171        To mix from 6 to 2 (or 1) channel(s) we have several possibilities (MPEG
   1172        DSE | MPEG PCE | ITU | ARIB | DLB). Use profile and the metadata
   1173        available flags to determine which equation to use: */
   1174 
   1175 #define DMX_METHOD_MPEG_AMD4 1
   1176 #define DMX_METHOD_MPEG_LEGACY 2
   1177 #define DMX_METHOD_ARIB_JAPAN 4
   1178 #define DMX_METHOD_ITU_RECOM 8
   1179 #define DMX_METHOD_CUSTOM 16
   1180 
   1181   dmxMethod = DMX_METHOD_MPEG_AMD4; /* default */
   1182 
   1183   if ((pParams->dmxProfile == DMX_PRFL_FORCE_MATRIX_MIX) &&
   1184       (pMetaData->typeFlags & TYPE_PCE_DATA)) {
   1185     dmxMethod = DMX_METHOD_MPEG_LEGACY;
   1186   } else if (!(pMetaData->typeFlags &
   1187                (TYPE_DSE_CLEV_DATA | TYPE_DSE_SLEV_DATA))) {
   1188     switch (pParams->dmxProfile) {
   1189       default:
   1190       case DMX_PRFL_STANDARD:
   1191         /* dmxMethod = DMX_METHOD_MPEG_AMD4; */
   1192         break;
   1193       case DMX_PRFL_MATRIX_MIX:
   1194       case DMX_PRFL_FORCE_MATRIX_MIX:
   1195         if (pMetaData->typeFlags & TYPE_PCE_DATA) {
   1196           dmxMethod = DMX_METHOD_MPEG_LEGACY;
   1197         }
   1198         break;
   1199       case DMX_PRFL_ARIB_JAPAN:
   1200         dmxMethod = DMX_METHOD_ARIB_JAPAN;
   1201         break;
   1202     }
   1203   }
   1204 
   1205   /* SECOND STAGE: */
   1206   if (numOutChannel <= TWO_CHANNEL) {
   1207     /* Create DMX matrix according to input configuration */
   1208     switch (inChMode) {
   1209       case CH_MODE_2_0_0_0: /* chCfg 2 */
   1210         /* Apply the dual channel mode. */
   1211         switch (pParams->dualChannelMode) {
   1212           case CH1_MODE: /* L' = 0.707 * Ch1;
   1213                             R' = 0.707 * Ch1; */
   1214             dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1215                           LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1216             dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1217                           LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1218             break;
   1219           case CH2_MODE: /* L' = 0.707 * Ch2;
   1220                             R' = 0.707 * Ch2; */
   1221             dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1222                           RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1223             dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1224                           RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1225             break;
   1226           case MIXED_MODE: /* L' = 0.5*Ch1 + 0.5*Ch2;
   1227                               R' = 0.5*Ch1 + 0.5*Ch2; */
   1228             dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1229                           LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0);
   1230             dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1231                           RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0);
   1232             dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1233                           LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0);
   1234             dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1235                           RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.5f), 0);
   1236             break;
   1237           default:
   1238           case STEREO_MODE:
   1239             /* Nothing to do */
   1240             break;
   1241         }
   1242         break;
   1243       /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1244        * - - - - - - - - - - - - - - - - - - - */
   1245       case CH_MODE_2_0_1_0: {
   1246         FIXP_DMX sMixLvl;
   1247         if (dmxMethod == DMX_METHOD_ARIB_JAPAN) {
   1248           /* L' = 0.707*L + 0.5*S;  R' = 0.707*R + 0.5*S; */
   1249           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1250                         LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1251           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1252                         RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1253           sMixLvl = FL2FXCONST_DMX(0.5f);
   1254         } else { /* L' = L + 0.707*S;  R' = R + 0.707*S; */
   1255           sMixLvl = FL2FXCONST_DMX(0.707f);
   1256         }
   1257         dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1258                       LEFT_REAR_CHANNEL, sMixLvl, 0);
   1259         dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1260                       LEFT_REAR_CHANNEL, sMixLvl, 0);
   1261       } break;
   1262       /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1263        * - - - - - - - - - - - - - - - - - - - */
   1264       case CH_MODE_3_0_0_0: /* chCfg 3 */
   1265       {
   1266         FIXP_DMX cMixLvl;
   1267         if (dmxMethod == DMX_METHOD_ARIB_JAPAN) {
   1268           /* L' = 0.707*L + 0.5*C;  R' = 0.707*R + 0.5*C; */
   1269           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1270                         LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1271           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1272                         RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1273           cMixLvl = FL2FXCONST_DMX(0.5f);
   1274         } else { /* L' = L + 0.707*C;  R' = R + 0.707*C; */
   1275           cMixLvl = FL2FXCONST_DMX(0.707f);
   1276         }
   1277         dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1278                       CENTER_FRONT_CHANNEL, cMixLvl, 0);
   1279         dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1280                       CENTER_FRONT_CHANNEL, cMixLvl, 0);
   1281       } break;
   1282       /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1283        * - - - - - - - - - - - - - - - - - - - */
   1284       case CH_MODE_3_0_1_0: /* chCfg 4 */
   1285       {
   1286         FIXP_DMX csMixLvl;
   1287         if (dmxMethod == DMX_METHOD_ARIB_JAPAN) {
   1288           /* L' = 0.707*L + 0.5*C + 0.5*S;  R' = 0.707*R + 0.5*C + 0.5*S; */
   1289           dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1290                         LEFT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1291           dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1292                         RIGHT_FRONT_CHANNEL, FL2FXCONST_DMX(0.707f), 0);
   1293           csMixLvl = FL2FXCONST_DMX(0.5f);
   1294         } else { /* L' = L + 0.707*C + 0.707*S;
   1295                     R' = R + 0.707*C + 0.707*S; */
   1296           csMixLvl = FL2FXCONST_DMX(0.707f);
   1297         }
   1298         dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1299                       CENTER_FRONT_CHANNEL, csMixLvl, 0);
   1300         dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1301                       LEFT_REAR_CHANNEL, csMixLvl, 0);
   1302         dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1303                       CENTER_FRONT_CHANNEL, csMixLvl, 0);
   1304         dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1305                       LEFT_REAR_CHANNEL, csMixLvl, 0);
   1306       } break;
   1307       /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   1308        * - - - - - - - - - - - - - - - - - - - */
   1309       case CH_MODE_3_0_2_0: /* chCfg 5 */
   1310       case CH_MODE_3_0_2_1: /* chCfg 6 */
   1311       {
   1312         switch (dmxMethod) {
   1313           default:
   1314           case DMX_METHOD_MPEG_AMD4: {
   1315             FIXP_DMX cMixLvl, sMixLvl, lMixLvl;
   1316             INT cMixScale, sMixScale, lMixScale;
   1317 
   1318             /* Get factors from meta data */
   1319             cMixLvl = abMixLvlValueTab[pMetaData->cLevIdx];
   1320             cMixScale = (pMetaData->cLevIdx == 0) ? 1 : 0;
   1321             sMixLvl = abMixLvlValueTab[pMetaData->sLevIdx];
   1322             sMixScale = (pMetaData->sLevIdx == 0) ? 1 : 0;
   1323             lMixLvl = lfeMixLvlValueTab[pMetaData->dmixIdxLfe];
   1324             if (pMetaData->dmixIdxLfe <= 1) {
   1325               lMixScale = 2;
   1326             } else if (pMetaData->dmixIdxLfe <= 5) {
   1327               lMixScale = 1;
   1328             } else {
   1329               lMixScale = 0;
   1330             }
   1331             /* Setup the DMX matrix */
   1332             if ((pParams->pseudoSurrMode == FORCE_PS_DMX) ||
   1333                 ((pParams->pseudoSurrMode == AUTO_PS_DMX) &&
   1334                  (pMetaData->pseudoSurround ==
   1335                   1))) { /* L' = L + C*clev - (Ls+Rs)*slev + LFE*lflev;
   1336                             R' = R + C*clev + (Ls+Rs)*slev + LFE*lflev; */
   1337               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1338                             CENTER_FRONT_CHANNEL, cMixLvl, cMixScale);
   1339               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1340                             LEFT_REAR_CHANNEL, -sMixLvl, sMixScale);
   1341               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1342                             RIGHT_REAR_CHANNEL, -sMixLvl, sMixScale);
   1343               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1344                             LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale);
   1345               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1346                             CENTER_FRONT_CHANNEL, cMixLvl, cMixScale);
   1347               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1348                             LEFT_REAR_CHANNEL, sMixLvl, sMixScale);
   1349               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1350                             RIGHT_REAR_CHANNEL, sMixLvl, sMixScale);
   1351               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1352                             LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale);
   1353             } else { /* L' = L + C*clev + Ls*slev + LFE*llev;
   1354                         R' = R + C*clev + Rs*slev + LFE*llev; */
   1355               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1356                             CENTER_FRONT_CHANNEL, cMixLvl, cMixScale);
   1357               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1358                             LEFT_REAR_CHANNEL, sMixLvl, sMixScale);
   1359               dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1360                             LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale);
   1361               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1362                             CENTER_FRONT_CHANNEL, cMixLvl, cMixScale);
   1363               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1364                             RIGHT_REAR_CHANNEL, sMixLvl, sMixScale);
   1365               dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1366                             LOW_FREQUENCY_CHANNEL, lMixLvl, lMixScale);
   1367             }
   1368 
   1369             /* Add additional DMX gain */
   1370             if (pMetaData->dmxGainIdx2 != 0) { /* Apply DMX gain 2 */
   1371               FIXP_DMX dmxGain;
   1372               INT dmxScale;
   1373               INT sign = (pMetaData->dmxGainIdx2 & 0x40) ? -1 : 1;
   1374               INT val = pMetaData->dmxGainIdx2 & 0x3F;
   1375 
   1376               /* 10^(dmx_gain_2/80) */
   1377               dmxGain = FX_DBL2FX_DMX(
   1378                   fLdPow(FL2FXCONST_DBL(0.830482023721841f), 2, /* log2(10) */
   1379                          (FIXP_DBL)(sign * val * (LONG)FL2FXCONST_DBL(0.0125f)),
   1380                          0, &dmxScale));
   1381               /* Currently only positive scale factors supported! */
   1382               if (dmxScale < 0) {
   1383                 dmxGain >>= -dmxScale;
   1384                 dmxScale = 0;
   1385               }
   1386 
   1387               dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1388                             LEFT_FRONT_CHANNEL, dmxGain, dmxScale);
   1389               dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1390                             RIGHT_FRONT_CHANNEL, dmxGain, dmxScale);
   1391             }
   1392           } break;
   1393           case DMX_METHOD_ARIB_JAPAN:
   1394           case DMX_METHOD_MPEG_LEGACY: {
   1395             FIXP_DMX flev, clev, slevLL, slevLR, slevRL, slevRR;
   1396             FIXP_DMX mtrxMixDwnCoef =
   1397                 mpegMixDownIdx2Coef[pMetaData->matrixMixdownIdx];
   1398 
   1399             if ((pParams->pseudoSurrMode == FORCE_PS_DMX) ||
   1400                 ((pParams->pseudoSurrMode == AUTO_PS_DMX) &&
   1401                  (pMetaData->pseudoSurround == 1))) {
   1402               if (dmxMethod == DMX_METHOD_ARIB_JAPAN) {
   1403                 /* 3/2 input: L' = 0.707 * [L+0.707*C-k*Ls-k*Rs];
   1404                               R' = 0.707 * [R+0.707*C+k*Ls+k*Rs]; */
   1405                 flev = mpegMixDownIdx2Coef[0]; /* a = 0.707 */
   1406               } else { /* 3/2 input: L' = (1.707+2*A)^-1 *
   1407                           [L+0.707*C-A*Ls-A*Rs]; R' = (1.707+2*A)^-1 *
   1408                           [R+0.707*C+A*Ls+A*Rs]; */
   1409                 flev = mpegMixDownIdx2PreFact[1][pMetaData->matrixMixdownIdx];
   1410               }
   1411               slevRR = slevRL = FX_DBL2FX_DMX(fMult(flev, mtrxMixDwnCoef));
   1412               slevLL = slevLR = -slevRL;
   1413             } else {
   1414               if (dmxMethod == DMX_METHOD_ARIB_JAPAN) {
   1415                 /* 3/2 input: L' = 0.707 * [L+0.707*C+k*Ls];
   1416                               R' = 0.707 * [R+0.707*C+k*Rs]; */
   1417                 flev = mpegMixDownIdx2Coef[0]; /* a = 0.707 */
   1418               } else { /* 3/2 input: L' = (1.707+A)^-1 * [L+0.707*C+A*Ls];
   1419                                      R' = (1.707+A)^-1 * [R+0.707*C+A*Rs]; */
   1420                 flev = mpegMixDownIdx2PreFact[0][pMetaData->matrixMixdownIdx];
   1421               }
   1422               slevRR = slevLL = FX_DBL2FX_DMX(fMult(flev, mtrxMixDwnCoef));
   1423               slevLR = slevRL = (FIXP_DMX)0;
   1424             }
   1425             /* common factor */
   1426             clev =
   1427                 FX_DBL2FX_DMX(fMult(flev, mpegMixDownIdx2Coef[0] /* 0.707 */));
   1428 
   1429             dmxSetChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1430                           LEFT_FRONT_CHANNEL, flev, 0);
   1431             dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1432                           CENTER_FRONT_CHANNEL, clev, 0);
   1433             dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1434                           LEFT_REAR_CHANNEL, slevLL, 0);
   1435             dmxAddChannel(mixFactors, mixScales, LEFT_FRONT_CHANNEL,
   1436                           RIGHT_REAR_CHANNEL, slevLR, 0);
   1437 
   1438             dmxSetChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1439                           RIGHT_FRONT_CHANNEL, flev, 0);
   1440             dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1441                           CENTER_FRONT_CHANNEL, clev, 0);
   1442             dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1443                           LEFT_REAR_CHANNEL, slevRL, 0);
   1444             dmxAddChannel(mixFactors, mixScales, RIGHT_FRONT_CHANNEL,
   1445                           RIGHT_REAR_CHANNEL, slevRR, 0);
   1446           } break;
   1447         } /* switch (dmxMethod) */
   1448       } break;
   1449       default:
   1450         /* This configuration does not fit to any known downmix equation! */
   1451         err = PCMDMX_INVALID_MODE;
   1452         break;
   1453     } /* switch (inChMode) */
   1454 
   1455     /* Mark the output channels */
   1456     FDKmemclear(valid, (8) * sizeof(unsigned int));
   1457     valid[LEFT_FRONT_CHANNEL] = 1;
   1458     valid[RIGHT_FRONT_CHANNEL] = 1;
   1459   }
   1460 
   1461   if (numOutChannel == ONE_CHANNEL) {
   1462     FIXP_DMX monoMixLevel;
   1463     INT monoMixScale = 0;
   1464 
   1465     dmxClearChannel(mixFactors, mixScales,
   1466                     CENTER_FRONT_CHANNEL); /* C is not in the mix */
   1467 
   1468     if (dmxMethod ==
   1469         DMX_METHOD_MPEG_LEGACY) { /* C' = (3+2*A)^-1 * [C+L+R+A*Ls+A+Rs]; */
   1470       monoMixLevel = mpegMixDownIdx2PreFact[2][pMetaData->matrixMixdownIdx];
   1471 
   1472       mixFactors[CENTER_FRONT_CHANNEL][CENTER_FRONT_CHANNEL] = monoMixLevel;
   1473       mixFactors[CENTER_FRONT_CHANNEL][LEFT_FRONT_CHANNEL] = monoMixLevel;
   1474       mixFactors[CENTER_FRONT_CHANNEL][RIGHT_FRONT_CHANNEL] = monoMixLevel;
   1475       monoMixLevel = FX_DBL2FX_DMX(fMult(
   1476           monoMixLevel, mpegMixDownIdx2Coef[pMetaData->matrixMixdownIdx]));
   1477       mixFactors[CENTER_FRONT_CHANNEL][LEFT_REAR_CHANNEL] = monoMixLevel;
   1478       mixFactors[CENTER_FRONT_CHANNEL][RIGHT_REAR_CHANNEL] = monoMixLevel;
   1479     } else {
   1480       switch (dmxMethod) {
   1481         case DMX_METHOD_MPEG_AMD4:
   1482           /* C' = L + R; */
   1483           monoMixLevel = FL2FXCONST_DMX(0.5f);
   1484           monoMixScale = 1;
   1485           break;
   1486         default:
   1487           /* C' = 0.5*L + 0.5*R; */
   1488           monoMixLevel = FL2FXCONST_DMX(0.5f);
   1489           monoMixScale = 0;
   1490           break;
   1491       }
   1492       dmxSetChannel(mixFactors, mixScales, CENTER_FRONT_CHANNEL,
   1493                     LEFT_FRONT_CHANNEL, monoMixLevel, monoMixScale);
   1494       dmxAddChannel(mixFactors, mixScales, CENTER_FRONT_CHANNEL,
   1495                     RIGHT_FRONT_CHANNEL, monoMixLevel, monoMixScale);
   1496     }
   1497 
   1498     /* Mark the output channel */
   1499     FDKmemclear(valid, (8) * sizeof(unsigned int));
   1500     valid[CENTER_FRONT_CHANNEL] = 1;
   1501   }
   1502 
   1503 #define MAX_SEARCH_START_VAL (-7)
   1504 
   1505   {
   1506     LONG chSum[(8)];
   1507     INT chSumMax = MAX_SEARCH_START_VAL;
   1508 
   1509     /* Determine the current maximum scale factor */
   1510     for (outCh = 0; outCh < (8); outCh += 1) {
   1511       if (valid[outCh] != 0) {
   1512         unsigned int inCh;
   1513         for (inCh = 0; inCh < (8); inCh += 1) {
   1514           if (mixScales[outCh][inCh] > maxScale) { /* Store the new maximum */
   1515             maxScale = mixScales[outCh][inCh];
   1516           }
   1517         }
   1518       }
   1519     }
   1520 
   1521     /* Individualy analyse output chanal levels */
   1522     for (outCh = 0; outCh < (8); outCh += 1) {
   1523       chSum[outCh] = MAX_SEARCH_START_VAL;
   1524       if (valid[outCh] != 0) {
   1525         int ovrflwProtScale = 0;
   1526         unsigned int inCh;
   1527 
   1528         /* Accumulate all factors for each output channel */
   1529         chSum[outCh] = 0;
   1530         for (inCh = 0; inCh < (8); inCh += 1) {
   1531           SHORT addFact = FX_DMX2SHRT(mixFactors[outCh][inCh]);
   1532           if (mixScales[outCh][inCh] <= maxScale) {
   1533             addFact >>= maxScale - mixScales[outCh][inCh];
   1534           } else {
   1535             addFact <<= mixScales[outCh][inCh] - maxScale;
   1536           }
   1537           chSum[outCh] += addFact;
   1538         }
   1539         if (chSum[outCh] > (LONG)MAXVAL_SGL) {
   1540           while (chSum[outCh] > (LONG)MAXVAL_SGL) {
   1541             ovrflwProtScale += 1;
   1542             chSum[outCh] >>= 1;
   1543           }
   1544         } else if (chSum[outCh] > 0) {
   1545           while ((chSum[outCh] << 1) <= (LONG)MAXVAL_SGL) {
   1546             ovrflwProtScale -= 1;
   1547             chSum[outCh] <<= 1;
   1548           }
   1549         }
   1550         /* Store the differential scaling in the same array */
   1551         chSum[outCh] = ovrflwProtScale;
   1552       }
   1553     }
   1554 
   1555     for (outCh = 0; outCh < (8); outCh += 1) {
   1556       if ((valid[outCh] != 0) &&
   1557           (chSum[outCh] > chSumMax)) { /* Store the new maximum */
   1558         chSumMax = chSum[outCh];
   1559       }
   1560     }
   1561     maxScale = fMax(maxScale + chSumMax, 0);
   1562 
   1563     /* Normalize all factors */
   1564     for (outCh = 0; outCh < (8); outCh += 1) {
   1565       if (valid[outCh] != 0) {
   1566         unsigned int inCh;
   1567         for (inCh = 0; inCh < (8); inCh += 1) {
   1568           if (mixFactors[outCh][inCh] != (FIXP_DMX)0) {
   1569             if (mixScales[outCh][inCh] <= maxScale) {
   1570               mixFactors[outCh][inCh] >>= maxScale - mixScales[outCh][inCh];
   1571             } else {
   1572               mixFactors[outCh][inCh] <<= mixScales[outCh][inCh] - maxScale;
   1573             }
   1574             mixScales[outCh][inCh] = maxScale;
   1575           }
   1576         }
   1577       }
   1578     }
   1579   }
   1580 
   1581   /* return the scale factor */
   1582   *pOutScale = maxScale;
   1583 
   1584   return (err);
   1585 }
   1586 
   1587 /** Open and initialize an instance of the PCM downmix module
   1588  * @param [out] Pointer to a buffer receiving the handle of the new instance.
   1589  * @returns Returns an error code.
   1590  **/
   1591 PCMDMX_ERROR pcmDmx_Open(HANDLE_PCM_DOWNMIX *pSelf) {
   1592   HANDLE_PCM_DOWNMIX self;
   1593 
   1594   if (pSelf == NULL) {
   1595     return (PCMDMX_INVALID_HANDLE);
   1596   }
   1597 
   1598   *pSelf = NULL;
   1599 
   1600   self = (HANDLE_PCM_DOWNMIX)GetPcmDmxInstance(0);
   1601   if (self == NULL) {
   1602     return (PCMDMX_OUT_OF_MEMORY);
   1603   }
   1604 
   1605   /* Reset the full instance */
   1606   pcmDmx_Reset(self, PCMDMX_RESET_FULL);
   1607 
   1608   *pSelf = self;
   1609 
   1610   return (PCMDMX_OK);
   1611 }
   1612 
   1613 /** Reset all static values like e.g. mixdown coefficients.
   1614  * @param [in] Handle of PCM downmix module instance.
   1615  * @param [in] Flags telling which parts of the module shall be reset.
   1616  * @returns Returns an error code.
   1617  **/
   1618 PCMDMX_ERROR pcmDmx_Reset(HANDLE_PCM_DOWNMIX self, UINT flags) {
   1619   if (self == NULL) {
   1620     return (PCMDMX_INVALID_HANDLE);
   1621   }
   1622 
   1623   if (flags & PCMDMX_RESET_PARAMS) {
   1624     PCM_DMX_USER_PARAMS *pParams = &self->userParams;
   1625 
   1626     pParams->dualChannelMode = STEREO_MODE;
   1627     pParams->pseudoSurrMode = NEVER_DO_PS_DMX;
   1628     pParams->numOutChannelsMax = (6);
   1629     pParams->numOutChannelsMin = (0);
   1630     pParams->frameDelay = 0;
   1631     pParams->expiryFrame = (0);
   1632 
   1633     self->applyProcessing = 0;
   1634   }
   1635 
   1636   if (flags & PCMDMX_RESET_BS_DATA) {
   1637     int slot;
   1638     /* Init all slots with a default set */
   1639     for (slot = 0; slot <= (1); slot += 1) {
   1640       FDKmemcpy(&self->bsMetaData[slot], &dfltMetaData,
   1641                 sizeof(DMX_BS_META_DATA));
   1642     }
   1643   }
   1644 
   1645   return (PCMDMX_OK);
   1646 }
   1647 
   1648 /** Set one parameter for one instance of the PCM downmix module.
   1649  * @param [in] Handle of PCM downmix module instance.
   1650  * @param [in] Parameter to be set.
   1651  * @param [in] Parameter value.
   1652  * @returns Returns an error code.
   1653  **/
   1654 PCMDMX_ERROR pcmDmx_SetParam(HANDLE_PCM_DOWNMIX self, const PCMDMX_PARAM param,
   1655                              const INT value) {
   1656   switch (param) {
   1657     case DMX_PROFILE_SETTING:
   1658       switch ((DMX_PROFILE_TYPE)value) {
   1659         case DMX_PRFL_STANDARD:
   1660         case DMX_PRFL_MATRIX_MIX:
   1661         case DMX_PRFL_FORCE_MATRIX_MIX:
   1662         case DMX_PRFL_ARIB_JAPAN:
   1663           break;
   1664         default:
   1665           return (PCMDMX_UNABLE_TO_SET_PARAM);
   1666       }
   1667       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1668       self->userParams.dmxProfile = (DMX_PROFILE_TYPE)value;
   1669       break;
   1670 
   1671     case DMX_BS_DATA_EXPIRY_FRAME:
   1672       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1673       self->userParams.expiryFrame = (value > 0) ? (UINT)value : 0;
   1674       break;
   1675 
   1676     case DMX_BS_DATA_DELAY:
   1677       if ((value > (1)) || (value < 0)) {
   1678         return (PCMDMX_UNABLE_TO_SET_PARAM);
   1679       }
   1680       if (self == NULL) {
   1681         return (PCMDMX_INVALID_HANDLE);
   1682       }
   1683       self->userParams.frameDelay = (UCHAR)value;
   1684       break;
   1685 
   1686     case MIN_NUMBER_OF_OUTPUT_CHANNELS:
   1687       switch (value) { /* supported output channels */
   1688         case -1:
   1689         case 0:
   1690         case ONE_CHANNEL:
   1691         case TWO_CHANNEL:
   1692         case SIX_CHANNEL:
   1693         case EIGHT_CHANNEL:
   1694           break;
   1695         default:
   1696           return (PCMDMX_UNABLE_TO_SET_PARAM);
   1697       }
   1698       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1699       /* Store the new value */
   1700       self->userParams.numOutChannelsMin = (value > 0) ? (SHORT)value : -1;
   1701       if ((value > 0) && (self->userParams.numOutChannelsMax > 0) &&
   1702           (value > self->userParams
   1703                        .numOutChannelsMax)) { /* MIN > MAX would be an invalid
   1704                                                  state. Thus set MAX = MIN in
   1705                                                  this case. */
   1706         self->userParams.numOutChannelsMax = self->userParams.numOutChannelsMin;
   1707       }
   1708       break;
   1709 
   1710     case MAX_NUMBER_OF_OUTPUT_CHANNELS:
   1711       switch (value) { /* supported output channels */
   1712         case -1:
   1713         case 0:
   1714         case ONE_CHANNEL:
   1715         case TWO_CHANNEL:
   1716         case SIX_CHANNEL:
   1717         case EIGHT_CHANNEL:
   1718           break;
   1719         default:
   1720           return (PCMDMX_UNABLE_TO_SET_PARAM);
   1721       }
   1722       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1723       /* Store the new value */
   1724       self->userParams.numOutChannelsMax = (value > 0) ? (SHORT)value : -1;
   1725       if ((value > 0) &&
   1726           (value < self->userParams
   1727                        .numOutChannelsMin)) { /* MAX < MIN would be an invalid
   1728                                                  state. Thus set MIN = MAX in
   1729                                                  this case. */
   1730         self->userParams.numOutChannelsMin = self->userParams.numOutChannelsMax;
   1731       }
   1732       break;
   1733 
   1734     case DMX_DUAL_CHANNEL_MODE:
   1735       switch ((DUAL_CHANNEL_MODE)value) {
   1736         case STEREO_MODE:
   1737         case CH1_MODE:
   1738         case CH2_MODE:
   1739         case MIXED_MODE:
   1740           break;
   1741         default:
   1742           return (PCMDMX_UNABLE_TO_SET_PARAM);
   1743       }
   1744       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1745       self->userParams.dualChannelMode = (DUAL_CHANNEL_MODE)value;
   1746       self->applyProcessing = ((DUAL_CHANNEL_MODE)value != STEREO_MODE)
   1747                                   ? 1
   1748                                   : 0; /* Force processing if necessary. */
   1749       break;
   1750 
   1751     case DMX_PSEUDO_SURROUND_MODE:
   1752       switch ((PSEUDO_SURROUND_MODE)value) {
   1753         case NEVER_DO_PS_DMX:
   1754         case AUTO_PS_DMX:
   1755         case FORCE_PS_DMX:
   1756           break;
   1757         default:
   1758           return (PCMDMX_UNABLE_TO_SET_PARAM);
   1759       }
   1760       if (self == NULL) return (PCMDMX_INVALID_HANDLE);
   1761       self->userParams.pseudoSurrMode = (PSEUDO_SURROUND_MODE)value;
   1762       break;
   1763 
   1764     default:
   1765       return (PCMDMX_UNKNOWN_PARAM);
   1766   }
   1767 
   1768   return (PCMDMX_OK);
   1769 }
   1770 
   1771 /** Get one parameter value of one PCM downmix module instance.
   1772  * @param [in] Handle of PCM downmix module instance.
   1773  * @param [in] Parameter to be set.
   1774  * @param [out] Pointer to buffer receiving the parameter value.
   1775  * @returns Returns an error code.
   1776  **/
   1777 PCMDMX_ERROR pcmDmx_GetParam(HANDLE_PCM_DOWNMIX self, const PCMDMX_PARAM param,
   1778                              INT *const pValue) {
   1779   PCM_DMX_USER_PARAMS *pUsrParams;
   1780 
   1781   if ((self == NULL) || (pValue == NULL)) {
   1782     return (PCMDMX_INVALID_HANDLE);
   1783   }
   1784   pUsrParams = &self->userParams;
   1785 
   1786   switch (param) {
   1787     case DMX_PROFILE_SETTING:
   1788       *pValue = (INT)pUsrParams->dmxProfile;
   1789       break;
   1790     case DMX_BS_DATA_EXPIRY_FRAME:
   1791       *pValue = (INT)pUsrParams->expiryFrame;
   1792       break;
   1793     case DMX_BS_DATA_DELAY:
   1794       *pValue = (INT)pUsrParams->frameDelay;
   1795       break;
   1796     case MIN_NUMBER_OF_OUTPUT_CHANNELS:
   1797       *pValue = (INT)pUsrParams->numOutChannelsMin;
   1798       break;
   1799     case MAX_NUMBER_OF_OUTPUT_CHANNELS:
   1800       *pValue = (INT)pUsrParams->numOutChannelsMax;
   1801       break;
   1802     case DMX_DUAL_CHANNEL_MODE:
   1803       *pValue = (INT)pUsrParams->dualChannelMode;
   1804       break;
   1805     case DMX_PSEUDO_SURROUND_MODE:
   1806       *pValue = (INT)pUsrParams->pseudoSurrMode;
   1807       break;
   1808     default:
   1809       return (PCMDMX_UNKNOWN_PARAM);
   1810   }
   1811 
   1812   return (PCMDMX_OK);
   1813 }
   1814 
   1815 /*
   1816  * Read DMX meta-data from a data stream element.
   1817  */
   1818 PCMDMX_ERROR pcmDmx_Parse(HANDLE_PCM_DOWNMIX self, HANDLE_FDK_BITSTREAM hBs,
   1819                           UINT ancDataBits, int isMpeg2) {
   1820   PCMDMX_ERROR errorStatus = PCMDMX_OK;
   1821 
   1822 #define MAX_DSE_ANC_BYTES (16)    /* 15 bytes */
   1823 #define ANC_DATA_SYNC_BYTE (0xBC) /* ancillary data sync byte. */
   1824 
   1825   DMX_BS_META_DATA *pBsMetaData;
   1826 
   1827   int skip4Dmx = 0, skip4Ext = 0;
   1828   int dmxLvlAvail = 0, extDataAvail = 0;
   1829   UINT foundNewData = 0;
   1830   UINT minAncBits = ((isMpeg2) ? 5 : 3) * 8;
   1831 
   1832   if ((self == NULL) || (hBs == NULL)) {
   1833     return (PCMDMX_INVALID_HANDLE);
   1834   }
   1835 
   1836   /* sanity checks */
   1837   if ((ancDataBits < minAncBits) || (ancDataBits > FDKgetValidBits(hBs))) {
   1838     return (PCMDMX_CORRUPT_ANC_DATA);
   1839   }
   1840 
   1841   pBsMetaData = &self->bsMetaData[0];
   1842 
   1843   if (isMpeg2) {
   1844     /* skip DVD ancillary data */
   1845     FDKpushFor(hBs, 16);
   1846   }
   1847 
   1848   /* check sync word */
   1849   if (FDKreadBits(hBs, 8) != ANC_DATA_SYNC_BYTE) {
   1850     return (PCMDMX_CORRUPT_ANC_DATA);
   1851   }
   1852 
   1853   /* skip MPEG audio type and Dolby surround mode */
   1854   FDKpushFor(hBs, 4);
   1855 
   1856   if (isMpeg2) {
   1857     /* int numAncBytes = */ FDKreadBits(hBs, 4);
   1858     /* advanced dynamic range control */
   1859     if (FDKreadBit(hBs)) skip4Dmx += 24;
   1860     /* dialog normalization */
   1861     if (FDKreadBit(hBs)) skip4Dmx += 8;
   1862     /* reproduction_level */
   1863     if (FDKreadBit(hBs)) skip4Dmx += 8;
   1864   } else {
   1865     FDKpushFor(hBs, 2); /* drc presentation mode */
   1866     pBsMetaData->pseudoSurround = (SCHAR)FDKreadBit(hBs);
   1867     FDKpushFor(hBs, 4); /* reserved bits */
   1868   }
   1869 
   1870   /* downmixing levels MPEGx status */
   1871   dmxLvlAvail = FDKreadBit(hBs);
   1872 
   1873   if (isMpeg2) {
   1874     /* scale factor CRC status */
   1875     if (FDKreadBit(hBs)) skip4Ext += 16;
   1876   } else {
   1877     /* ancillary data extension status */
   1878     extDataAvail = FDKreadBit(hBs);
   1879   }
   1880 
   1881   /* audio coding and compression status */
   1882   if (FDKreadBit(hBs)) skip4Ext += 16;
   1883   /* coarse grain timecode status */
   1884   if (FDKreadBit(hBs)) skip4Ext += 16;
   1885   /* fine grain timecode status */
   1886   if (FDKreadBit(hBs)) skip4Ext += 16;
   1887 
   1888   /* skip the useless data to get to the DMX levels */
   1889   FDKpushFor(hBs, skip4Dmx);
   1890 
   1891   /* downmix_levels_MPEGX */
   1892   if (dmxLvlAvail) {
   1893     if (FDKreadBit(hBs)) { /* center_mix_level_on */
   1894       pBsMetaData->cLevIdx = (UCHAR)FDKreadBits(hBs, 3);
   1895       foundNewData |= TYPE_DSE_CLEV_DATA;
   1896     } else {
   1897       FDKreadBits(hBs, 3);
   1898     }
   1899     if (FDKreadBit(hBs)) { /* surround_mix_level_on */
   1900       pBsMetaData->sLevIdx = (UCHAR)FDKreadBits(hBs, 3);
   1901       foundNewData |= TYPE_DSE_SLEV_DATA;
   1902     } else {
   1903       FDKreadBits(hBs, 3);
   1904     }
   1905   }
   1906 
   1907   /* skip the useless data to get to the ancillary data extension */
   1908   FDKpushFor(hBs, skip4Ext);
   1909 
   1910   /* anc data extension (MPEG-4 only) */
   1911   if (extDataAvail) {
   1912     int extDmxLvlSt, extDmxGainSt, extDmxLfeSt;
   1913 
   1914     FDKreadBit(hBs); /* reserved bit */
   1915     extDmxLvlSt = FDKreadBit(hBs);
   1916     extDmxGainSt = FDKreadBit(hBs);
   1917     extDmxLfeSt = FDKreadBit(hBs);
   1918     FDKreadBits(hBs, 4); /* reserved bits */
   1919 
   1920     if (extDmxLvlSt) {
   1921       pBsMetaData->dmixIdxA = (UCHAR)FDKreadBits(hBs, 3);
   1922       pBsMetaData->dmixIdxB = (UCHAR)FDKreadBits(hBs, 3);
   1923       FDKreadBits(hBs, 2); /* reserved bits */
   1924       foundNewData |= TYPE_DSE_DMIX_AB_DATA;
   1925     }
   1926     if (extDmxGainSt) {
   1927       pBsMetaData->dmxGainIdx5 = (UCHAR)FDKreadBits(hBs, 7);
   1928       FDKreadBit(hBs); /* reserved bit */
   1929       pBsMetaData->dmxGainIdx2 = (UCHAR)FDKreadBits(hBs, 7);
   1930       FDKreadBit(hBs); /* reserved bit */
   1931       foundNewData |= TYPE_DSE_DMX_GAIN_DATA;
   1932     }
   1933     if (extDmxLfeSt) {
   1934       pBsMetaData->dmixIdxLfe = (UCHAR)FDKreadBits(hBs, 4);
   1935       FDKreadBits(hBs, 4); /* reserved bits */
   1936       foundNewData |= TYPE_DSE_DMIX_LFE_DATA;
   1937     }
   1938   }
   1939 
   1940   /* final sanity check on the amount of read data */
   1941   if ((INT)FDKgetValidBits(hBs) < 0) {
   1942     errorStatus = PCMDMX_CORRUPT_ANC_DATA;
   1943   }
   1944 
   1945   if ((errorStatus == PCMDMX_OK) && (foundNewData != 0)) {
   1946     /* announce new data */
   1947     pBsMetaData->typeFlags |= foundNewData;
   1948     /* reset expiry counter */
   1949     pBsMetaData->expiryCount = 0;
   1950   }
   1951 
   1952   return (errorStatus);
   1953 }
   1954 
   1955 /*
   1956  * Read DMX meta-data from a data stream element.
   1957  */
   1958 PCMDMX_ERROR pcmDmx_ReadDvbAncData(HANDLE_PCM_DOWNMIX self, UCHAR *pAncDataBuf,
   1959                                    UINT ancDataBytes, int isMpeg2) {
   1960   PCMDMX_ERROR errorStatus = PCMDMX_OK;
   1961   FDK_BITSTREAM bs;
   1962   HANDLE_FDK_BITSTREAM hBs = &bs;
   1963 
   1964   if (self == NULL) {
   1965     return (PCMDMX_INVALID_HANDLE);
   1966   }
   1967 
   1968   /* sanity checks */
   1969   if ((pAncDataBuf == NULL) || (ancDataBytes == 0)) {
   1970     return (PCMDMX_CORRUPT_ANC_DATA);
   1971   }
   1972 
   1973   FDKinitBitStream(hBs, pAncDataBuf, MAX_DSE_ANC_BYTES, ancDataBytes * 8,
   1974                    BS_READER);
   1975 
   1976   errorStatus = pcmDmx_Parse(self, hBs, ancDataBytes * 8, isMpeg2);
   1977 
   1978   return (errorStatus);
   1979 }
   1980 
   1981 /** Set the matrix mixdown information extracted from the PCE of an AAC
   1982  *bitstream. Note: Call only if matrix_mixdown_idx_present is true.
   1983  * @param [in] Handle of PCM downmix module instance.
   1984  * @param [in] The 2 bit matrix mixdown index extracted from PCE.
   1985  * @param [in] The pseudo surround enable flag extracted from PCE.
   1986  * @returns Returns an error code.
   1987  **/
   1988 PCMDMX_ERROR pcmDmx_SetMatrixMixdownFromPce(HANDLE_PCM_DOWNMIX self,
   1989                                             int matrixMixdownPresent,
   1990                                             int matrixMixdownIdx,
   1991                                             int pseudoSurroundEnable) {
   1992   if (self == NULL) {
   1993     return (PCMDMX_INVALID_HANDLE);
   1994   }
   1995 
   1996   {
   1997     DMX_BS_META_DATA *pBsMetaData = &self->bsMetaData[0];
   1998 
   1999     if (matrixMixdownPresent) {
   2000       pBsMetaData->pseudoSurround = (pseudoSurroundEnable) ? 1 : 0;
   2001       pBsMetaData->matrixMixdownIdx = matrixMixdownIdx & 0x03;
   2002       pBsMetaData->typeFlags |= TYPE_PCE_DATA;
   2003       /* Reset expiry counter */
   2004       pBsMetaData->expiryCount = 0;
   2005     }
   2006   }
   2007 
   2008   return (PCMDMX_OK);
   2009 }
   2010 
   2011 /** Apply down or up mixing.
   2012  * @param [in]    Handle of PCM downmix module instance.
   2013  * @param [inout] Pointer to buffer that hold the time domain signal.
   2014  * @param [in]    Pointer where the amount of output samples is returned into.
   2015  * @param [in]    Size of pPcmBuf.
   2016  * @param [inout] Pointer where the amount of output channels is returned into.
   2017  * @param [in]    Input and output samples are processed interleaved.
   2018  * @param [inout] Array where the corresponding channel type for each output
   2019  *audio channel is stored into.
   2020  * @param [inout] Array where the corresponding channel type index for each
   2021  *output audio channel is stored into.
   2022  * @param [in]    Array containing the out channel mapping to be used (From MPEG
   2023  *PCE ordering to whatever is required).
   2024  * @param [out]   Pointer on a field receiving the scale factor that has to be
   2025  *applied on all samples afterwards. If the handed pointer is NULL scaling is
   2026  *done internally.
   2027  * @returns Returns an error code.
   2028  **/
   2029 PCMDMX_ERROR pcmDmx_ApplyFrame(HANDLE_PCM_DOWNMIX self, DMX_PCM *pPcmBuf,
   2030                                const int pcmBufSize, UINT frameSize,
   2031                                INT *nChannels, INT fInterleaved,
   2032                                AUDIO_CHANNEL_TYPE channelType[],
   2033                                UCHAR channelIndices[],
   2034                                const FDK_channelMapDescr *const mapDescr,
   2035                                INT *pDmxOutScale) {
   2036   PCM_DMX_USER_PARAMS *pParam = NULL;
   2037   PCMDMX_ERROR errorStatus = PCMDMX_OK;
   2038   DUAL_CHANNEL_MODE dualChannelMode;
   2039   PCM_DMX_CHANNEL_MODE inChMode;
   2040   PCM_DMX_CHANNEL_MODE outChMode;
   2041   INT devNull; /* Just a dummy to avoid a lot of branches in the code */
   2042   int numOutChannels, numInChannels;
   2043   int inStride, outStride, offset;
   2044   int dmxMaxScale, dmxScale;
   2045   int slot;
   2046   UCHAR inOffsetTable[(8)];
   2047 
   2048   DMX_BS_META_DATA bsMetaData;
   2049 
   2050   if ((self == NULL) || (nChannels == NULL) || (channelType == NULL) ||
   2051       (channelIndices == NULL) || (!FDK_chMapDescr_isValid(mapDescr))) {
   2052     return (PCMDMX_INVALID_HANDLE);
   2053   }
   2054 
   2055   /* Init the output scaling */
   2056   dmxScale = 0;
   2057   if (pDmxOutScale != NULL) {
   2058     /* Avoid final scaling internally and hand it to the outside world. */
   2059     *pDmxOutScale = 0;
   2060     dmxMaxScale = (3);
   2061   } else {
   2062     /* Apply the scaling internally. */
   2063     pDmxOutScale = &devNull; /* redirect to temporal stack memory */
   2064     dmxMaxScale = 0;
   2065   }
   2066 
   2067   pParam = &self->userParams;
   2068   numInChannels = *nChannels;
   2069 
   2070   /* Perform some input sanity checks */
   2071   if (pPcmBuf == NULL) {
   2072     return (PCMDMX_INVALID_ARGUMENT);
   2073   }
   2074   if (frameSize == 0) {
   2075     return (PCMDMX_INVALID_ARGUMENT);
   2076   }
   2077   if (numInChannels == 0) {
   2078     return (PCMDMX_INVALID_ARGUMENT);
   2079   }
   2080   if (numInChannels > (8)) {
   2081     return (PCMDMX_INVALID_CH_CONFIG);
   2082   }
   2083 
   2084   /* Check on misconfiguration */
   2085   FDK_ASSERT((pParam->numOutChannelsMax <= 0) ||
   2086              (pParam->numOutChannelsMax >= pParam->numOutChannelsMin));
   2087 
   2088   /* Determine if the module has to do processing */
   2089   if ((self->applyProcessing == 0) &&
   2090       ((pParam->numOutChannelsMax <= 0) ||
   2091        (pParam->numOutChannelsMax >= numInChannels)) &&
   2092       (pParam->numOutChannelsMin <= numInChannels)) {
   2093     /* Nothing to do */
   2094     return (errorStatus);
   2095   }
   2096 
   2097   /* Determine the number of output channels */
   2098   if ((pParam->numOutChannelsMax > 0) &&
   2099       (numInChannels > pParam->numOutChannelsMax)) {
   2100     numOutChannels = pParam->numOutChannelsMax;
   2101   } else if (numInChannels < pParam->numOutChannelsMin) {
   2102     numOutChannels = pParam->numOutChannelsMin;
   2103   } else {
   2104     numOutChannels = numInChannels;
   2105   }
   2106 
   2107   /* Check I/O buffer size */
   2108   if ((UINT)pcmBufSize < (UINT)numOutChannels * frameSize) {
   2109     return (PCMDMX_OUTPUT_BUFFER_TOO_SMALL);
   2110   }
   2111 
   2112   dualChannelMode = pParam->dualChannelMode;
   2113 
   2114   /* Analyse input channel configuration and get channel offset
   2115    * table that can be accessed with the fixed channel labels. */
   2116   errorStatus = getChannelMode(numInChannels, channelType, channelIndices,
   2117                                inOffsetTable, &inChMode);
   2118   if (PCMDMX_IS_FATAL_ERROR(errorStatus) || (inChMode == CH_MODE_UNDEFINED)) {
   2119     /* We don't need to restore because the channel
   2120        configuration has not been changed. Just exit. */
   2121     return (PCMDMX_INVALID_CH_CONFIG);
   2122   }
   2123 
   2124   /* Set input stride and offset */
   2125   if (fInterleaved) {
   2126     inStride = numInChannels;
   2127     offset = 1; /* Channel specific offset factor */
   2128   } else {
   2129     inStride = 1;
   2130     offset = frameSize; /* Channel specific offset factor */
   2131   }
   2132 
   2133   /* Reset downmix meta data if necessary */
   2134   if ((pParam->expiryFrame > 0) &&
   2135       (++self->bsMetaData[0].expiryCount >
   2136        pParam
   2137            ->expiryFrame)) { /* The metadata read from bitstream is too old. */
   2138 #ifdef FDK_ASSERT_ENABLE
   2139     PCMDMX_ERROR err = pcmDmx_Reset(self, PCMDMX_RESET_BS_DATA);
   2140     FDK_ASSERT(err == PCMDMX_OK);
   2141 #else
   2142     pcmDmx_Reset(self, PCMDMX_RESET_BS_DATA);
   2143 #endif
   2144   }
   2145   FDKmemcpy(&bsMetaData, &self->bsMetaData[pParam->frameDelay],
   2146             sizeof(DMX_BS_META_DATA));
   2147   /* Maintain delay line */
   2148   for (slot = pParam->frameDelay; slot > 0; slot -= 1) {
   2149     FDKmemcpy(&self->bsMetaData[slot], &self->bsMetaData[slot - 1],
   2150               sizeof(DMX_BS_META_DATA));
   2151   }
   2152 
   2153   /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2154    * - - - - - - - - - - - - - - - - - - */
   2155   if (numInChannels > numOutChannels) { /* Apply downmix */
   2156     DMX_PCM *pInPcm[(8)] = {NULL};
   2157     DMX_PCM *pOutPcm[(8)] = {NULL};
   2158     FIXP_DMX mixFactors[(8)][(8)];
   2159     UCHAR outOffsetTable[(8)];
   2160     UINT sample;
   2161     int chCfg = 0;
   2162     int bypScale = 0;
   2163 
   2164     if (numInChannels > SIX_CHANNEL) {
   2165       AUDIO_CHANNEL_TYPE multiPurposeChType[2];
   2166 
   2167       /* Get the type of the multipurpose channels */
   2168       multiPurposeChType[0] =
   2169           channelType[inOffsetTable[LEFT_MULTIPRPS_CHANNEL]];
   2170       multiPurposeChType[1] =
   2171           channelType[inOffsetTable[RIGHT_MULTIPRPS_CHANNEL]];
   2172 
   2173       /* Check if the input configuration is one defined in the standard. */
   2174       switch (inChMode) {
   2175         case CH_MODE_5_0_2_1: /* chCfg 7 || 14 */
   2176           /* Further analyse the input config to distinguish the two
   2177            * CH_MODE_5_0_2_1 configs. */
   2178           if ((multiPurposeChType[0] == ACT_FRONT_TOP) &&
   2179               (multiPurposeChType[1] == ACT_FRONT_TOP)) {
   2180             chCfg = 14;
   2181           } else {
   2182             chCfg = 7;
   2183           }
   2184           break;
   2185         case CH_MODE_3_0_3_1: /* chCfg 11 */
   2186           chCfg = 11;
   2187           break;
   2188         case CH_MODE_3_0_4_1: /* chCfg 12 */
   2189           chCfg = 12;
   2190           break;
   2191         default:
   2192           chCfg = 0; /* Not a known config */
   2193           break;
   2194       }
   2195     }
   2196 
   2197     /* Set this stages output stride and channel mode: */
   2198     outStride = (fInterleaved) ? numOutChannels : 1;
   2199     outChMode = outChModeTable[numOutChannels];
   2200     FDK_ASSERT(outChMode != CH_MODE_UNDEFINED);
   2201 
   2202     /* Get channel description and channel mapping for the desired output
   2203      * configuration. */
   2204     getChannelDescription(outChMode, mapDescr, channelType, channelIndices,
   2205                           outOffsetTable);
   2206     /* Now there is no way back because we modified the channel configuration!
   2207      */
   2208 
   2209     /* Create the DMX matrix */
   2210     errorStatus =
   2211         getMixFactors((chCfg > 0) ? 1 : 0,
   2212                       (chCfg > 0) ? (PCM_DMX_CHANNEL_MODE)chCfg : inChMode,
   2213                       outChMode, pParam, &bsMetaData, mixFactors, &dmxScale);
   2214     /* No fatal errors can occur here. The function is designed to always return
   2215        a valid matrix. The error code is used to signal configurations and
   2216        matrices that are not conform to any standard. */
   2217 
   2218     /* Determine the final scaling */
   2219     bypScale = fMin(dmxMaxScale, dmxScale);
   2220     *pDmxOutScale += bypScale;
   2221     dmxScale -= bypScale;
   2222 
   2223     { /* Set channel pointer for input. Remove empty cols. */
   2224       int inCh, outCh, map[(8)];
   2225       int ch = 0;
   2226       for (inCh = 0; inCh < (8); inCh += 1) {
   2227         if (inOffsetTable[inCh] < (UCHAR)numInChannels) {
   2228           pInPcm[ch] = &pPcmBuf[inOffsetTable[inCh] * offset];
   2229           map[ch++] = inCh;
   2230         }
   2231       }
   2232       for (; ch < (8); ch += 1) {
   2233         map[ch] = ch;
   2234       }
   2235 
   2236       /* Remove unused cols from factor matrix */
   2237       for (inCh = 0; inCh < numInChannels; inCh += 1) {
   2238         if (inCh != map[inCh]) {
   2239           for (outCh = 0; outCh < (8); outCh += 1) {
   2240             mixFactors[outCh][inCh] = mixFactors[outCh][map[inCh]];
   2241           }
   2242         }
   2243       }
   2244 
   2245       /* Set channel pointer for output. Remove empty cols. */
   2246       ch = 0;
   2247       for (outCh = 0; outCh < (8); outCh += 1) {
   2248         if (outOffsetTable[outCh] < (UCHAR)numOutChannels) {
   2249           pOutPcm[ch] = &pPcmBuf[outOffsetTable[outCh] * offset];
   2250           map[ch++] = outCh;
   2251         }
   2252       }
   2253       for (; ch < (8); ch += 1) {
   2254         map[ch] = ch;
   2255       }
   2256 
   2257       /* Remove unused rows from factor matrix */
   2258       for (outCh = 0; outCh < numOutChannels; outCh += 1) {
   2259         if (outCh != map[outCh]) {
   2260           FDKmemcpy(&mixFactors[outCh], &mixFactors[map[outCh]],
   2261                     (8) * sizeof(FIXP_DMX));
   2262         }
   2263       }
   2264     }
   2265 
   2266     /* Sample processing loop */
   2267     for (sample = 0; sample < frameSize; sample++) {
   2268       DMX_PCM tIn[(8)] = {0};
   2269       FIXP_DBL tOut[(8)] = {(FIXP_DBL)0};
   2270       int inCh, outCh;
   2271 
   2272       /* Preload all input samples */
   2273       for (inCh = 0; inCh < numInChannels; inCh += 1) {
   2274         if (pInPcm[inCh] != NULL) {
   2275           tIn[inCh] = *pInPcm[inCh];
   2276           pInPcm[inCh] += inStride;
   2277         } else {
   2278           tIn[inCh] = (DMX_PCM)0;
   2279         }
   2280       }
   2281       /* Apply downmix coefficients to input samples and accumulate for output
   2282        */
   2283       for (outCh = 0; outCh < numOutChannels; outCh += 1) {
   2284         for (inCh = 0; inCh < numInChannels; inCh += 1) {
   2285           tOut[outCh] += fMult((DMX_PCMF)tIn[inCh], mixFactors[outCh][inCh]);
   2286         }
   2287         FDK_ASSERT(pOutPcm[outCh] >= pPcmBuf);
   2288         FDK_ASSERT(pOutPcm[outCh] < &pPcmBuf[pcmBufSize]);
   2289         /* Write sample */
   2290         *pOutPcm[outCh] = (DMX_PCM)SATURATE_SHIFT(
   2291             tOut[outCh], DFRACT_BITS - DMX_PCM_BITS - dmxScale, DMX_PCM_BITS);
   2292         pOutPcm[outCh] += outStride;
   2293       }
   2294     }
   2295 
   2296     /* Update the number of output channels */
   2297     *nChannels = numOutChannels;
   2298 
   2299   } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2300        - - - - - - - - - - - - - - - - - - */
   2301   else if (numInChannels < numOutChannels) { /* Apply rudimentary upmix */
   2302     /* Set up channel pointer */
   2303     UCHAR outOffsetTable[(8)];
   2304 
   2305     /* FIRST STAGE
   2306          Create a stereo/dual channel signal */
   2307     if (numInChannels == ONE_CHANNEL) {
   2308       DMX_PCM *pInPcm[(8)];
   2309       DMX_PCM *pOutLF, *pOutRF;
   2310       UINT sample;
   2311 
   2312       /* Set this stages output stride and channel mode: */
   2313       outStride = (fInterleaved) ? TWO_CHANNEL : 1;
   2314       outChMode = outChModeTable[TWO_CHANNEL];
   2315 
   2316       /* Get channel description and channel mapping for this
   2317        * stages number of output channels (always STEREO). */
   2318       getChannelDescription(outChMode, mapDescr, channelType, channelIndices,
   2319                             outOffsetTable);
   2320       /* Now there is no way back because we modified the channel configuration!
   2321        */
   2322 
   2323       /* Set input channel pointer. The first channel is always at index 0. */
   2324       pInPcm[CENTER_FRONT_CHANNEL] =
   2325           &pPcmBuf[(frameSize - 1) *
   2326                    inStride]; /* Considering input mapping could lead to a
   2327                                  invalid pointer here if the channel is not
   2328                                  declared to be a front channel. */
   2329 
   2330       /* Set output channel pointer (for this stage). */
   2331       pOutLF = &pPcmBuf[outOffsetTable[LEFT_FRONT_CHANNEL] * offset +
   2332                         (frameSize - 1) * outStride];
   2333       pOutRF = &pPcmBuf[outOffsetTable[RIGHT_FRONT_CHANNEL] * offset +
   2334                         (frameSize - 1) * outStride];
   2335 
   2336       /* 1/0 input: */
   2337       for (sample = 0; sample < frameSize; sample++) {
   2338         /* L' = C;  R' = C; */
   2339         *pOutLF = *pOutRF = *pInPcm[CENTER_FRONT_CHANNEL];
   2340 
   2341         pInPcm[CENTER_FRONT_CHANNEL] -= inStride;
   2342         pOutLF -= outStride;
   2343         pOutRF -= outStride;
   2344       }
   2345 
   2346       /* Prepare for next stage: */
   2347       inStride = outStride;
   2348       inChMode = outChMode;
   2349       FDKmemcpy(inOffsetTable, outOffsetTable, (8) * sizeof(UCHAR));
   2350     }
   2351 
   2352     /* SECOND STAGE
   2353          Extend with zero channels to achieved the desired number of output
   2354        channels. */
   2355     if (numOutChannels > TWO_CHANNEL) {
   2356       DMX_PCM *pIn[(8)] = {NULL};
   2357       DMX_PCM *pOut[(8)] = {NULL};
   2358       UINT sample;
   2359       AUDIO_CHANNEL_TYPE inChTypes[(8)];
   2360       UCHAR inChIndices[(8)];
   2361       UCHAR numChPerGrp[2][(4)];
   2362       int nContentCh = 0; /* Number of channels with content */
   2363       int nEmptyCh = 0;   /* Number of channels with content */
   2364       int ch, chGrp, isCompatible = 1;
   2365 
   2366       /* Do not change the signalling which is the channel types and indices.
   2367          Just reorder and add channels. So first save the input signalling. */
   2368       FDKmemcpy(inChTypes, channelType,
   2369                 numInChannels * sizeof(AUDIO_CHANNEL_TYPE));
   2370       FDKmemclear(inChTypes + numInChannels,
   2371                   ((8) - numInChannels) * sizeof(AUDIO_CHANNEL_TYPE));
   2372       FDKmemcpy(inChIndices, channelIndices, numInChannels * sizeof(UCHAR));
   2373       FDKmemclear(inChIndices + numInChannels,
   2374                   ((8) - numInChannels) * sizeof(UCHAR));
   2375 
   2376       /* Set this stages output stride and channel mode: */
   2377       outStride = (fInterleaved) ? numOutChannels : 1;
   2378       outChMode = outChModeTable[numOutChannels];
   2379       FDK_ASSERT(outChMode != CH_MODE_UNDEFINED);
   2380 
   2381       /* Check if input channel config can be easily mapped to the desired
   2382        * output config. */
   2383       for (chGrp = 0; chGrp < (4); chGrp += 1) {
   2384         numChPerGrp[IN][chGrp] = (inChMode >> (chGrp * 4)) & 0xF;
   2385         numChPerGrp[OUT][chGrp] = (outChMode >> (chGrp * 4)) & 0xF;
   2386 
   2387         if (numChPerGrp[IN][chGrp] > numChPerGrp[OUT][chGrp]) {
   2388           isCompatible = 0;
   2389           break;
   2390         }
   2391       }
   2392 
   2393       if (isCompatible) {
   2394         /* Get new channel description and channel
   2395          * mapping for the desired output channel mode. */
   2396         getChannelDescription(outChMode, mapDescr, channelType, channelIndices,
   2397                               outOffsetTable);
   2398         /* If the input config has a back center channel but the output
   2399            config has not, copy it to left and right (if available). */
   2400         if ((numChPerGrp[IN][CH_GROUP_REAR] % 2) &&
   2401             !(numChPerGrp[OUT][CH_GROUP_REAR] % 2)) {
   2402           if (numChPerGrp[IN][CH_GROUP_REAR] == 1) {
   2403             inOffsetTable[RIGHT_REAR_CHANNEL] =
   2404                 inOffsetTable[LEFT_REAR_CHANNEL];
   2405           } else if (numChPerGrp[IN][CH_GROUP_REAR] == 3) {
   2406             inOffsetTable[RIGHT_MULTIPRPS_CHANNEL] =
   2407                 inOffsetTable[LEFT_MULTIPRPS_CHANNEL];
   2408           }
   2409         }
   2410       } else {
   2411         /* Just copy and extend the original config */
   2412         FDKmemcpy(outOffsetTable, inOffsetTable, (8) * sizeof(UCHAR));
   2413       }
   2414 
   2415       /* Set I/O channel pointer.
   2416          Note: The following assignment algorithm clears the channel offset
   2417          tables. Thus they can not be used afterwards. */
   2418       for (ch = 0; ch < (8); ch += 1) {
   2419         if ((outOffsetTable[ch] < 255) &&
   2420             (inOffsetTable[ch] < 255)) { /* Set I/O pointer: */
   2421           pIn[nContentCh] =
   2422               &pPcmBuf[inOffsetTable[ch] * offset + (frameSize - 1) * inStride];
   2423           pOut[nContentCh] = &pPcmBuf[outOffsetTable[ch] * offset +
   2424                                       (frameSize - 1) * outStride];
   2425           /* Update signalling */
   2426           channelType[outOffsetTable[ch]] = inChTypes[inOffsetTable[ch]];
   2427           channelIndices[outOffsetTable[ch]] = inChIndices[inOffsetTable[ch]];
   2428           inOffsetTable[ch] = 255;
   2429           outOffsetTable[ch] = 255;
   2430           nContentCh += 1;
   2431         }
   2432       }
   2433       if (isCompatible) {
   2434         /* Assign the remaining input channels.
   2435            This is just a safety appliance. We should never need it. */
   2436         for (ch = 0; ch < (8); ch += 1) {
   2437           if (inOffsetTable[ch] < 255) {
   2438             int outCh;
   2439             for (outCh = 0; outCh < (8); outCh += 1) {
   2440               if (outOffsetTable[outCh] < 255) {
   2441                 break;
   2442               }
   2443             }
   2444             if (outCh >= (8)) {
   2445               FDK_ASSERT(0);
   2446               break;
   2447             }
   2448             /* Set I/O pointer: */
   2449             pIn[nContentCh] = &pPcmBuf[inOffsetTable[ch] * offset +
   2450                                        (frameSize - 1) * inStride];
   2451             pOut[nContentCh] = &pPcmBuf[outOffsetTable[outCh] * offset +
   2452                                         (frameSize - 1) * outStride];
   2453             /* Update signalling */
   2454             FDK_ASSERT(inOffsetTable[outCh] < numInChannels);
   2455             FDK_ASSERT(outOffsetTable[outCh] < numOutChannels);
   2456             channelType[outOffsetTable[outCh]] = inChTypes[inOffsetTable[ch]];
   2457             channelIndices[outOffsetTable[outCh]] =
   2458                 inChIndices[inOffsetTable[ch]];
   2459             inOffsetTable[ch] = 255;
   2460             outOffsetTable[outCh] = 255;
   2461             nContentCh += 1;
   2462           }
   2463         }
   2464         /* Set the remaining output channel pointer */
   2465         for (ch = 0; ch < (8); ch += 1) {
   2466           if (outOffsetTable[ch] < 255) {
   2467             pOut[nContentCh + nEmptyCh] = &pPcmBuf[outOffsetTable[ch] * offset +
   2468                                                    (frameSize - 1) * outStride];
   2469             /* Expand output signalling */
   2470             channelType[outOffsetTable[ch]] = ACT_NONE;
   2471             channelIndices[outOffsetTable[ch]] = (UCHAR)nEmptyCh;
   2472             outOffsetTable[ch] = 255;
   2473             nEmptyCh += 1;
   2474           }
   2475         }
   2476       } else {
   2477         /* Set the remaining output channel pointer */
   2478         for (ch = nContentCh; ch < numOutChannels; ch += 1) {
   2479           pOut[ch] = &pPcmBuf[ch * offset + (frameSize - 1) * outStride];
   2480           /* Expand output signalling */
   2481           channelType[ch] = ACT_NONE;
   2482           channelIndices[ch] = (UCHAR)nEmptyCh;
   2483           nEmptyCh += 1;
   2484         }
   2485       }
   2486 
   2487       /* First copy the channels that have signal */
   2488       for (sample = 0; sample < frameSize; sample += 1) {
   2489         DMX_PCM tIn[(8)];
   2490         /* Read all channel samples */
   2491         for (ch = 0; ch < nContentCh; ch += 1) {
   2492           tIn[ch] = *pIn[ch];
   2493           pIn[ch] -= inStride;
   2494         }
   2495         /* Write all channel samples */
   2496         for (ch = 0; ch < nContentCh; ch += 1) {
   2497           *pOut[ch] = tIn[ch];
   2498           pOut[ch] -= outStride;
   2499         }
   2500       }
   2501 
   2502       /* Clear all the other channels */
   2503       for (sample = 0; sample < frameSize; sample++) {
   2504         for (ch = nContentCh; ch < numOutChannels; ch += 1) {
   2505           *pOut[ch] = (DMX_PCM)0;
   2506           pOut[ch] -= outStride;
   2507         }
   2508       }
   2509     }
   2510 
   2511     /* update the number of output channels */
   2512     *nChannels = numOutChannels;
   2513   } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   2514        - - - - - - - - - - - - - - - - - - */
   2515   else if (numInChannels == numOutChannels) {
   2516     /* Don't need to change the channel description here */
   2517 
   2518     switch (numInChannels) {
   2519       case 2: { /* Set up channel pointer */
   2520         DMX_PCM *pInPcm[(8)];
   2521         DMX_PCM *pOutL, *pOutR;
   2522         FIXP_DMX flev;
   2523 
   2524         UINT sample;
   2525 
   2526         if (fInterleaved) {
   2527           inStride = numInChannels;
   2528           outStride =
   2529               2; /* fixed !!! (below stereo is donwmixed to mono if required */
   2530           offset = 1; /* Channel specific offset factor */
   2531         } else {
   2532           inStride = 1;
   2533           outStride = 1;
   2534           offset = frameSize; /* Channel specific offset factor */
   2535         }
   2536 
   2537         /* Set input channel pointer */
   2538         pInPcm[LEFT_FRONT_CHANNEL] =
   2539             &pPcmBuf[inOffsetTable[LEFT_FRONT_CHANNEL] * offset];
   2540         pInPcm[RIGHT_FRONT_CHANNEL] =
   2541             &pPcmBuf[inOffsetTable[RIGHT_FRONT_CHANNEL] * offset];
   2542 
   2543         /* Set output channel pointer (same as input) */
   2544         pOutL = pInPcm[LEFT_FRONT_CHANNEL];
   2545         pOutR = pInPcm[RIGHT_FRONT_CHANNEL];
   2546 
   2547         /* Set downmix levels: */
   2548         flev = FL2FXCONST_DMX(0.70710678f);
   2549         /* 2/0 input: */
   2550         switch (dualChannelMode) {
   2551           case CH1_MODE: /* L' = 0.707 * Ch1;  R' = 0.707 * Ch1 */
   2552             for (sample = 0; sample < frameSize; sample++) {
   2553               *pOutL = *pOutR = (DMX_PCM)SATURATE_RIGHT_SHIFT(
   2554                   fMult((DMX_PCMF)*pInPcm[LEFT_FRONT_CHANNEL], flev),
   2555                   DFRACT_BITS - DMX_PCM_BITS, DMX_PCM_BITS);
   2556 
   2557               pInPcm[LEFT_FRONT_CHANNEL] += inStride;
   2558               pOutL += outStride;
   2559               pOutR += outStride;
   2560             }
   2561             break;
   2562           case CH2_MODE: /* L' = 0.707 * Ch2;  R' = 0.707 * Ch2 */
   2563             for (sample = 0; sample < frameSize; sample++) {
   2564               *pOutL = *pOutR = (DMX_PCM)SATURATE_RIGHT_SHIFT(
   2565                   fMult((DMX_PCMF)*pInPcm[RIGHT_FRONT_CHANNEL], flev),
   2566                   DFRACT_BITS - DMX_PCM_BITS, DMX_PCM_BITS);
   2567 
   2568               pInPcm[RIGHT_FRONT_CHANNEL] += inStride;
   2569               pOutL += outStride;
   2570               pOutR += outStride;
   2571             }
   2572             break;
   2573           case MIXED_MODE: /* L' = 0.5*Ch1 + 0.5*Ch2;  R' = 0.5*Ch1 + 0.5*Ch2 */
   2574             for (sample = 0; sample < frameSize; sample++) {
   2575               *pOutL = *pOutR = (*pInPcm[LEFT_FRONT_CHANNEL] >> 1) +
   2576                                 (*pInPcm[RIGHT_FRONT_CHANNEL] >> 1);
   2577 
   2578               pInPcm[LEFT_FRONT_CHANNEL] += inStride;
   2579               pInPcm[RIGHT_FRONT_CHANNEL] += inStride;
   2580               pOutL += outStride;
   2581               pOutR += outStride;
   2582             }
   2583             break;
   2584           default:
   2585           case STEREO_MODE:
   2586             /* nothing to do */
   2587             break;
   2588         }
   2589       } break;
   2590 
   2591       default:
   2592         /* nothing to do */
   2593         break;
   2594     }
   2595   }
   2596 
   2597   return (errorStatus);
   2598 }
   2599 
   2600 /** Close an instance of the PCM downmix module.
   2601  * @param [inout] Pointer to a buffer containing the handle of the instance.
   2602  * @returns Returns an error code.
   2603  **/
   2604 PCMDMX_ERROR pcmDmx_Close(HANDLE_PCM_DOWNMIX *pSelf) {
   2605   if (pSelf == NULL) {
   2606     return (PCMDMX_INVALID_HANDLE);
   2607   }
   2608 
   2609   FreePcmDmxInstance(pSelf);
   2610   *pSelf = NULL;
   2611 
   2612   return (PCMDMX_OK);
   2613 }
   2614 
   2615 /** Get library info for this module.
   2616  * @param [out] Pointer to an allocated LIB_INFO structure.
   2617  * @returns Returns an error code.
   2618  */
   2619 PCMDMX_ERROR pcmDmx_GetLibInfo(LIB_INFO *info) {
   2620   int i;
   2621 
   2622   if (info == NULL) {
   2623     return PCMDMX_INVALID_ARGUMENT;
   2624   }
   2625 
   2626   /* Search for next free tab */
   2627   for (i = 0; i < FDK_MODULE_LAST; i++) {
   2628     if (info[i].module_id == FDK_NONE) break;
   2629   }
   2630   if (i == FDK_MODULE_LAST) {
   2631     return PCMDMX_INVALID_ARGUMENT;
   2632   }
   2633 
   2634   /* Add the library info */
   2635   info[i].module_id = FDK_PCMDMX;
   2636   info[i].version =
   2637       LIB_VERSION(PCMUTIL_LIB_VL0, PCMUTIL_LIB_VL1, PCMUTIL_LIB_VL2);
   2638   LIB_VERSION_STRING(info + i);
   2639   info[i].build_date = PCMUTIL_LIB_BUILD_DATE;
   2640   info[i].build_time = PCMUTIL_LIB_BUILD_TIME;
   2641   info[i].title = PCMDMX_LIB_TITLE;
   2642 
   2643   /* Set flags */
   2644   info[i].flags = 0 | CAPF_DMX_BLIND /* At least blind downmixing is possible */
   2645                   | CAPF_DMX_PCE     /* Guided downmix with data from MPEG-2/4
   2646                                         Program Config Elements (PCE). */
   2647                   | CAPF_DMX_ARIB /* PCE guided downmix with slightly different
   2648                                      equations and levels. */
   2649                   | CAPF_DMX_DVB  /* Guided downmix with data from DVB ancillary
   2650                                      data fields. */
   2651                   | CAPF_DMX_CH_EXP /* Simple upmixing by dublicating channels
   2652                                        or adding zero channels. */
   2653                   | CAPF_DMX_6_CH | CAPF_DMX_8_CH;
   2654 
   2655   /* Add lib info for FDK tools (if not yet done). */
   2656   FDK_toolsGetLibInfo(info);
   2657 
   2658   return PCMDMX_OK;
   2659 }
   2660