summaryrefslogtreecommitdiffstats
path: root/libPCMutils/src
diff options
context:
space:
mode:
authorDave Burke <daveburke@google.com>2012-04-17 09:51:45 -0700
committerDave Burke <daveburke@google.com>2012-04-17 23:04:43 -0700
commit9bf37cc9712506b2483650c82d3c41152337ef7e (patch)
tree77db44e2bae06e3d144b255628be2b7a55c581d3 /libPCMutils/src
parenta37315fe10ee143d6d0b28c19d41a476a23e63ea (diff)
downloadfdk-aac-9bf37cc9712506b2483650c82d3c41152337ef7e.tar.gz
fdk-aac-9bf37cc9712506b2483650c82d3c41152337ef7e.tar.bz2
fdk-aac-9bf37cc9712506b2483650c82d3c41152337ef7e.zip
Fraunhofer AAC codec.
License boilerplate update to follow. Change-Id: I2810460c11a58b6d148d84673cc031f3685e79b5
Diffstat (limited to 'libPCMutils/src')
-rw-r--r--libPCMutils/src/Android.mk16
-rw-r--r--libPCMutils/src/pcmutils_lib.cpp1219
2 files changed, 1235 insertions, 0 deletions
diff --git a/libPCMutils/src/Android.mk b/libPCMutils/src/Android.mk
new file mode 100644
index 0000000..174581c
--- /dev/null
+++ b/libPCMutils/src/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := pcmutils_lib.cpp
+
+LOCAL_CFLAGS := -DANDROID
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/../include \
+ $(LOCAL_PATH)/../../libSYS/include \
+ $(LOCAL_PATH)/../../libFDK/include
+
+LOCAL_MODULE:= libPCMutils
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libPCMutils/src/pcmutils_lib.cpp b/libPCMutils/src/pcmutils_lib.cpp
new file mode 100644
index 0000000..1beec6a
--- /dev/null
+++ b/libPCMutils/src/pcmutils_lib.cpp
@@ -0,0 +1,1219 @@
+/**************************** FDK PCM utils module **************************
+
+ (C) Copyright Fraunhofer IIS 2008
+ All Rights Reserved
+
+ Please be advised that this software and/or program delivery is
+ Confidential Information of Fraunhofer and subject to and covered by the
+
+ Fraunhofer IIS Software Evaluation Agreement
+ between Google Inc. and Fraunhofer
+ effective and in full force since March 1, 2012.
+
+ You may use this software and/or program only under the terms and
+ conditions described in the above mentioned Fraunhofer IIS Software
+ Evaluation Agreement. Any other and/or further use requires a separate agreement.
+
+
+ $Id$
+ Author(s): Christian Griebel
+ Description: Defines functions to interface with the PCM post processing
+ module.
+
+ This software and/or program is protected by copyright law and international
+ treaties. Any reproduction or distribution of this software and/or program,
+ or any portion of it, may result in severe civil and criminal penalties, and
+ will be prosecuted to the maximum extent possible under law.
+
+*******************************************************************************/
+
+#include "pcmutils_lib.h"
+
+#include "genericStds.h"
+#include "fixpoint_math.h"
+
+/* Decoder library info */
+#define PCMDMX_LIB_VL0 2
+#define PCMDMX_LIB_VL1 2
+#define PCMDMX_LIB_VL2 0
+#define PCMDMX_LIB_TITLE "PCM Downmix Lib"
+#define PCMDMX_LIB_BUILD_DATE __DATE__
+#define PCMDMX_LIB_BUILD_TIME __TIME__
+
+/* Library settings */
+#define PCM_DMX_MAX_DELAY_FRAMES ( 1 )
+#define PCM_DMX_MAX_CHANNELS ( 8 )
+#define PCM_DMX_MAX_CHANNEL_GROUPS ( 4 )
+#define PCM_DMX_MAX_CHANNELS_PER_GROUP ( 3 ) /* The maximum over all groups */
+#define PCMDMX_DFLT_EXPIRY_FRAME ( 40 ) /* At least 400ms (FL 960 @ 96kHz) */
+
+/* Fixed and unique channel group indices.
+ * The last group index has to be smaller than PCM_DMX_MAX_CHANNEL_GROUPS. */
+#define CH_GROUP_FRONT ( 0 )
+#define CH_GROUP_SIDE ( 1 )
+#define CH_GROUP_REAR ( 2 )
+#define CH_GROUP_LFE ( 3 )
+
+/* The ordering of the following fixed channel labels has to be in MPEG-4 style.
+ * From the center to the back with left and right channel interleaved (starting with left).
+ * The last channel label index has to be smaller than PCM_DMX_MAX_CHANNELS. */
+#define CENTER_FRONT_CHANNEL ( 0 ) /* C */
+#define LEFT_FRONT_CHANNEL ( 1 ) /* L */
+#define RIGHT_FRONT_CHANNEL ( 2 ) /* R */
+#define LEFT_OUTSIDE_CHANNEL ( 3 ) /* Lo */
+#define RIGHT_OUTSIDE_CHANNEL ( 4 ) /* Ro */
+#define LEFT_REAR_CHANNEL ( 5 ) /* Lr aka left back channel */
+#define RIGHT_REAR_CHANNEL ( 6 ) /* Rr aka right back channel */
+#define LOW_FREQUENCY_CHANNEL ( 7 ) /* Lf */
+
+/* More constants */
+#define ANC_DATA_SYNC_BYTE ( 0xBC ) /* ancillary data sync byte. */
+#define ATTENUATION_FACTOR_1 ( FL2FXCONST_SGL(0.70710678f) )
+#define TWO_CHANNEL ( 2 )
+
+/* Sanity checks on library setting: */
+
+/* List of packed channel modes */
+typedef enum
+{ /* CH_MODE_<numFrontCh>_<numOutsideCh>_<numRearCh>_<numLfCh> */
+ CH_MODE_UNDEFINED = 0x0000,
+ /* 1 channel */
+ CH_MODE_1_0_0_0 = 0x0001, /* chCfg 1 */
+ /* 2 channels */
+ CH_MODE_2_0_0_0 = 0x0002, /* chCfg 2 */
+ /* 3 channels */
+ CH_MODE_3_0_0_0 = 0x0003, /* chCfg 3 */
+ CH_MODE_2_0_1_0 = 0x0102,
+ CH_MODE_2_0_0_1 = 0x1002,
+ /* 4 channels */
+ CH_MODE_3_0_1_0 = 0x0103, /* chCfg 4 */
+ CH_MODE_2_0_2_0 = 0x0202,
+ CH_MODE_2_0_1_1 = 0x1102,
+ /* 5 channels */
+ CH_MODE_3_0_2_0 = 0x0203, /* chCfg 5 */
+ CH_MODE_2_0_2_1 = 0x1202,
+ CH_MODE_3_0_1_1 = 0x1103,
+ CH_MODE_3_2_0_0 = 0x0023,
+ /* 6 channels */
+ CH_MODE_3_0_2_1 = 0x1203, /* chCfg 6 */
+ CH_MODE_3_2_1_0 = 0x0123,
+ /* 7 channels */
+ CH_MODE_2_2_2_1 = 0x1222,
+ CH_MODE_3_2_1_1 = 0x1123,
+ CH_MODE_3_2_2_0 = 0x0223,
+ /* 8 channels */
+ CH_MODE_3_2_2_1 = 0x1222, /* chCfg 7 */
+ CH_MODE_3_2_1_2 = 0x2123,
+ CH_MODE_2_2_2_2 = 0x2222
+
+} PCM_DMX_CHANNEL_MODE;
+
+
+/* These are the channel configurations linked to
+ the number of output channels give by the user: */
+static const PCM_DMX_CHANNEL_MODE outChModeTable[PCM_DMX_MAX_CHANNELS] =
+{
+ CH_MODE_1_0_0_0, /* 1 channel */
+ CH_MODE_2_0_0_0, /* 2 channels */
+ CH_MODE_3_0_0_0, /* 3 channels */
+ CH_MODE_3_0_1_0, /* 4 channels */
+ CH_MODE_3_0_2_0, /* 5 channels */
+ CH_MODE_3_0_2_1, /* 6 channels */
+ CH_MODE_3_2_2_0, /* 7 channels */
+ CH_MODE_3_2_2_1 /* 8 channels */
+};
+
+static const FIXP_SGL dvbDownmixFactors[8] =
+{
+ FL2FXCONST_SGL(1.0f),
+ FL2FXCONST_SGL(0.841f),
+ FL2FXCONST_SGL(0.707f),
+ FL2FXCONST_SGL(0.596f),
+ FL2FXCONST_SGL(0.500f),
+ FL2FXCONST_SGL(0.422f),
+ FL2FXCONST_SGL(0.355f),
+ FL2FXCONST_SGL(0.0f)
+};
+
+
+ /* MPEG matrix mixdown:
+ Set 1: L' = (1 + 2^-0.5 + A )^-1 * [L + C * 2^-0.5 + A * Ls];
+ R' = (1 + 2^-0.5 + A )^-1 * [R + C * 2^-0.5 + A * Rs];
+
+ Set 2: L' = (1 + 2^-0.5 + 2A )^-1 * [L + C * 2^-0.5 - A * (Ls + Rs)];
+ R' = (1 + 2^-0.5 + 2A )^-1 * [R + C * 2^-0.5 + A * (Ls + Rs)];
+
+ M = (3 + 2A)^-1 * [L + C + R + A*(Ls + Rs)];
+ */
+ static const FIXP_SGL mpegMixDownIdx2Coef[4] =
+ {
+ FL2FXCONST_SGL(0.70710678f),
+ FL2FXCONST_SGL(0.5f),
+ FL2FXCONST_SGL(0.35355339f),
+ FL2FXCONST_SGL(0.0f)
+ };
+
+ static const FIXP_SGL mpegMixDownIdx2PreFact[4] =
+ {
+ FL2FXCONST_SGL(0.4142135623730950f),
+ FL2FXCONST_SGL(0.4530818393219728f),
+ FL2FXCONST_SGL(0.4852813742385703f),
+ FL2FXCONST_SGL(0.5857864376269050f)
+ };
+
+ typedef struct
+ {
+ USHORT matrixMixdownIdx; /*!< MPEG mixdown index extracted from PCE. */
+ USHORT pseudoSurroundEnable; /*!< Pseudo surround enable flag extracted from PCE. */
+ USHORT mixdownAvailable; /*!< Will be set to 1 if we found a valid coefficient. */
+
+ } MPEG_MIXDOWN_INFO;
+
+
+typedef struct
+{
+ FIXP_SGL centerMixLevelValue; /*!< DVB mixdown level for the center channel extracted from anc data. */
+ FIXP_SGL surroundMixLevelValue; /*!< DVB mixdown level for back channels extracted from anc data. */
+
+ UCHAR mixLevelsAvail; /*!< Will be set to 1 if we found a valid coefficient. */
+
+} DVB_MIXDOWN_LEVELS;
+
+
+/* Modules main data structure: */
+struct PCM_DMX_INSTANCE
+{
+ DVB_MIXDOWN_LEVELS dvbMixDownLevels[PCM_DMX_MAX_DELAY_FRAMES+1];
+ MPEG_MIXDOWN_INFO mpegMixDownInfo[PCM_DMX_MAX_DELAY_FRAMES+1];
+ DUAL_CHANNEL_MODE dualChannelMode;
+ UINT expiryFrame;
+ UINT expiryCount;
+ SHORT numOutputChannels;
+ UCHAR applyProcessing;
+ UCHAR frameDelay;
+};
+
+/* Memory allocation macro */
+C_ALLOC_MEM_STATIC(PcmDmxInstance, struct PCM_DMX_INSTANCE, 1)
+
+
+/** Evaluate a given channel configuration and extract a packed channel mode and generate a channel offset table
+ * This function is the inverse to the getChannelDescription() routine.
+ * @param [in] The total number of channels of the given configuration.
+ * @param [in] Array holding the corresponding channel types for each channel.
+ * @param [in] Array holding the corresponding channel type indices for each channel.
+ * @param [in] Array containing the channel mapping to be used (From MPEG PCE ordering to whatever is required).
+ * @param [out] Array where the buffer offsets for each channel are stored into.
+ * @returns Returns the packed channel mode.
+ **/
+static
+PCM_DMX_CHANNEL_MODE getChannelMode (
+ const INT numChannels, /* in */
+ const AUDIO_CHANNEL_TYPE channelType[], /* in */
+ const UCHAR channelIndices[], /* in */
+ const UCHAR channelMapping[PCM_DMX_MAX_CHANNELS], /* in */
+ UCHAR offsetTable[PCM_DMX_MAX_CHANNELS] /* out */
+ )
+{
+ UINT chMode = CH_MODE_UNDEFINED;
+ UCHAR chIdx[PCM_DMX_MAX_CHANNEL_GROUPS][PCM_DMX_MAX_CHANNELS_PER_GROUP];
+ UCHAR numChInGrp[PCM_DMX_MAX_CHANNEL_GROUPS];
+ int ch, grpIdx, err = 0;
+
+ FDK_ASSERT(channelType != NULL);
+ FDK_ASSERT(channelIndices != NULL);
+ FDK_ASSERT(channelMapping != NULL);
+ FDK_ASSERT(offsetTable != NULL);
+
+ /* For details see ISO/IEC 13818-7:2005(E), 8.5.3 Channel configuration */
+ FDKmemclear(numChInGrp, PCM_DMX_MAX_CHANNEL_GROUPS*sizeof(UCHAR));
+ FDKmemset(offsetTable, 255, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR));
+
+ /* Categorize channels */
+ for (ch = 0; ch < numChannels; ch += 1) {
+ int i = 0, j, chGrpIdx = channelIndices[ch];
+
+ switch (channelType[ch]) {
+ case ACT_FRONT:
+ case ACT_FRONT_TOP:
+ grpIdx = CH_GROUP_FRONT;
+ break;
+ case ACT_SIDE:
+ case ACT_SIDE_TOP:
+ grpIdx = CH_GROUP_SIDE;
+ break;
+ case ACT_BACK:
+ case ACT_BACK_TOP:
+ grpIdx = CH_GROUP_REAR;
+ break;
+ case ACT_LFE:
+ grpIdx = CH_GROUP_LFE;
+ break;
+ default:
+ err = -1;
+ continue;
+ }
+
+ if (numChInGrp[grpIdx] < PCM_DMX_MAX_CHANNELS_PER_GROUP) {
+ /* Sort channels by index */
+ while ( (i < numChInGrp[grpIdx]) && (chGrpIdx > channelIndices[chIdx[grpIdx][i]]) ) {
+ i += 1;
+ }
+ for (j = numChInGrp[grpIdx]; j > i; j -= 1) {
+ chIdx[grpIdx][j] = chIdx[grpIdx][j-1];
+ }
+ chIdx[grpIdx][i] = ch;
+ numChInGrp[grpIdx] += 1;
+ }
+ }
+
+ /* Compose channel offset table */
+
+ /* Non-symmetric channels */
+ if (numChInGrp[CH_GROUP_FRONT] & 0x1) {
+ /* Odd number of front channels -> we have a center channel.
+ In MPEG-4 the center has the index 0. */
+ offsetTable[CENTER_FRONT_CHANNEL] = channelMapping[chIdx[CH_GROUP_FRONT][0]];
+ }
+
+ for (grpIdx = 0; grpIdx < PCM_DMX_MAX_CHANNEL_GROUPS; grpIdx += 1) {
+ int chMapPos, maxChannels = 0;
+ ch = 0;
+
+ switch (grpIdx) {
+ case CH_GROUP_FRONT:
+ chMapPos = LEFT_FRONT_CHANNEL;
+ maxChannels = 3;
+ ch = numChInGrp[grpIdx] & 0x1;
+ break;
+ case CH_GROUP_SIDE:
+ chMapPos = LEFT_OUTSIDE_CHANNEL;
+ maxChannels = 2;
+ break;
+ case CH_GROUP_REAR:
+ chMapPos = LEFT_REAR_CHANNEL;
+ maxChannels = 2;
+ break;
+ case CH_GROUP_LFE:
+ chMapPos = LOW_FREQUENCY_CHANNEL;
+ maxChannels = 1;
+ break;
+ default:
+ err = -1;
+ continue;
+ }
+
+ for ( ; ch < numChInGrp[grpIdx]; ch += 1) {
+ if (ch < maxChannels) {
+ offsetTable[chMapPos] = channelMapping[chIdx[grpIdx][ch]];
+ chMapPos += 1;
+ } else {
+ err = -1;
+ }
+ }
+ }
+
+ if (err == 0) {
+ /* Compose the channel mode */
+ chMode = (numChInGrp[CH_GROUP_LFE] & 0xF) << 12
+ | (numChInGrp[CH_GROUP_REAR] & 0xF) << 8
+ | (numChInGrp[CH_GROUP_SIDE] & 0xF) << 4
+ | (numChInGrp[CH_GROUP_FRONT] & 0xF);
+ }
+
+ return (PCM_DMX_CHANNEL_MODE)chMode;
+}
+
+
+/** Generate a channel offset table and complete channel description for a given (packed) channel mode.
+ * This function is the inverse to the getChannelMode() routine.
+ * @param [in] The total number of channels of the given configuration.
+ * @param [in] Array containing the channel mapping to be used (From MPEG PCE ordering to whatever is required).
+ * @param [out] Array where corresponding channel types for each channels are stored into.
+ * @param [out] Array where corresponding channel type indices for each output channel are stored into.
+ * @param [out] Array where the buffer offsets for each channel are stored into.
+ * @returns None.
+ **/
+void getChannelDescription (
+ const PCM_DMX_CHANNEL_MODE chMode, /* in */
+ const UCHAR channelMapping[][PCM_DMX_MAX_CHANNELS], /* in */
+ AUDIO_CHANNEL_TYPE channelType[], /* out */
+ UCHAR channelIndices[], /* out */
+ UCHAR offsetTable[PCM_DMX_MAX_CHANNELS] /* out */
+ )
+{
+ const UCHAR *pChannelMap;
+ int grpIdx, ch = 0, numChannels = 0;
+ UCHAR numChInGrp[PCM_DMX_MAX_CHANNEL_GROUPS];
+
+ FDK_ASSERT(channelType != NULL);
+ FDK_ASSERT(channelIndices != NULL);
+ FDK_ASSERT(channelMapping != NULL);
+ FDK_ASSERT(offsetTable != NULL);
+
+ /* Init output arrays */
+ FDKmemclear(channelType, PCM_DMX_MAX_CHANNELS*sizeof(AUDIO_CHANNEL_TYPE));
+ FDKmemclear(channelIndices, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR));
+ FDKmemset(offsetTable, 255, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR));
+
+ /* Extract the number of channels per group */
+ numChInGrp[CH_GROUP_FRONT] = chMode & 0xF;
+ numChInGrp[CH_GROUP_SIDE] = (chMode >> 4) & 0xF;
+ numChInGrp[CH_GROUP_REAR] = (chMode >> 8) & 0xF;
+ numChInGrp[CH_GROUP_LFE] = (chMode >> 12) & 0xF;
+
+ /* Summerize to get the total number of channels */
+ for (grpIdx = 0; grpIdx < PCM_DMX_MAX_CHANNEL_GROUPS; grpIdx += 1) {
+ numChannels += numChInGrp[grpIdx];
+ }
+
+ /* Get the appropriate channel map */
+ pChannelMap = channelMapping[numChannels-1];
+
+ /* Compose channel offset table */
+
+ /* Non-symmetric channels */
+ if (numChInGrp[CH_GROUP_FRONT] & 0x1) {
+ /* Odd number of front channels -> we have a center channel.
+ In MPEG-4 the center has the index 0. */
+ offsetTable[CENTER_FRONT_CHANNEL] = pChannelMap[0];
+ channelType[0] = ACT_FRONT;
+ ch += 1;
+ }
+
+ for (grpIdx = 0; grpIdx < PCM_DMX_MAX_CHANNEL_GROUPS; grpIdx += 1) {
+ AUDIO_CHANNEL_TYPE type;
+ int chMapPos, maxChannels = 0;
+ int chIdx = 0;
+
+ switch (grpIdx) {
+ case CH_GROUP_FRONT:
+ type = ACT_FRONT;
+ chMapPos = LEFT_FRONT_CHANNEL;
+ maxChannels = 3;
+ chIdx = numChInGrp[grpIdx] & 0x1;
+ break;
+ case CH_GROUP_SIDE:
+ type = ACT_SIDE;
+ chMapPos = LEFT_OUTSIDE_CHANNEL;
+ maxChannels = 2;
+ break;
+ case CH_GROUP_REAR:
+ type = ACT_BACK;
+ chMapPos = LEFT_REAR_CHANNEL;
+ maxChannels = 2;
+ break;
+ case CH_GROUP_LFE:
+ type = ACT_LFE;
+ chMapPos = LOW_FREQUENCY_CHANNEL;
+ maxChannels = 1;
+ break;
+ default:
+ break;
+ }
+
+ for ( ; (chIdx < numChInGrp[grpIdx]) && (chIdx < maxChannels); chIdx += 1) {
+ offsetTable[chMapPos] = pChannelMap[ch];
+ channelType[ch] = type;
+ channelIndices[ch] = chIdx;
+ chMapPos += 1;
+ ch += 1;
+ }
+ }
+}
+
+
+/** Open and initialize an instance of the PCM downmix module
+ * @param [out] Pointer to a buffer receiving the handle of the new instance.
+ * @returns Returns an error code.
+ **/
+PCMDMX_ERROR pcmDmx_Open (
+ HANDLE_PCM_DOWNMIX *pSelf
+ )
+{
+ HANDLE_PCM_DOWNMIX self;
+
+ if (pSelf == NULL) {
+ return (PCMDMX_INVALID_HANDLE);
+ }
+
+ *pSelf = NULL;
+
+ self = (HANDLE_PCM_DOWNMIX) GetPcmDmxInstance( 0 );
+ if (self == NULL) {
+ return (PCMDMX_OUT_OF_MEMORY);
+ }
+
+ /* Reset the full instance */
+ pcmDmx_Reset( self, PCMDMX_RESET_FULL );
+
+ *pSelf = self;
+
+ return (PCMDMX_OK);
+}
+
+
+/** Reset all static values like e.g. mixdown coefficients.
+ * @param [in] Handle of PCM downmix module instance.
+ * @param [in] Flags telling which parts of the module shall be reset.
+ * @returns Returns an error code.
+ **/
+PCMDMX_ERROR pcmDmx_Reset (
+ HANDLE_PCM_DOWNMIX self,
+ UINT flags
+ )
+{
+ if (self == NULL) { return (PCMDMX_INVALID_HANDLE); }
+
+ if (flags & PCMDMX_RESET_PARAMS) {
+ self->dualChannelMode = STEREO_MODE;
+ self->numOutputChannels = 0;
+ self->applyProcessing = 0;
+ self->frameDelay = 0;
+ self->expiryFrame = PCMDMX_DFLT_EXPIRY_FRAME;
+ }
+
+ if (flags & PCMDMX_RESET_BS_DATA) {
+ int slot;
+ for (slot = 0; slot <= PCM_DMX_MAX_DELAY_FRAMES; slot += 1) {
+ self->dvbMixDownLevels[slot].centerMixLevelValue = dvbDownmixFactors[2]; /* 0.707 */
+ self->dvbMixDownLevels[slot].surroundMixLevelValue = dvbDownmixFactors[0]; /* 1.000 */
+ self->dvbMixDownLevels[slot].mixLevelsAvail = 0;
+
+ self->mpegMixDownInfo[slot].mixdownAvailable = 0;
+ }
+ /* Reset expiry counter */
+ self->expiryCount = 0;
+ }
+
+ return (PCMDMX_OK);
+}
+
+
+/** Set one parameter for one instance of the PCM downmix module.
+ * @param [in] Handle of PCM downmix module instance.
+ * @param [in] Parameter to be set.
+ * @param [in] Parameter value.
+ * @returns Returns an error code.
+ **/
+PCMDMX_ERROR pcmDmx_SetParam (
+ HANDLE_PCM_DOWNMIX self,
+ PCMDMX_PARAM param,
+ UINT value
+ )
+{
+ switch (param)
+ {
+ case DMX_BS_DATA_EXPIRY_FRAME:
+ if (self == NULL)
+ return (PCMDMX_INVALID_HANDLE);
+ self->expiryFrame = value;
+ break;
+
+ case DMX_BS_DATA_DELAY:
+ if (value > PCM_DMX_MAX_DELAY_FRAMES) {
+ return (PCMDMX_UNABLE_TO_SET_PARAM);
+ }
+ if (self == NULL) {
+ return (PCMDMX_INVALID_HANDLE);
+ }
+ self->frameDelay = value;
+ break;
+
+ case NUMBER_OF_OUTPUT_CHANNELS:
+ switch ((int)value) { /* supported output channels */
+ case -1: case 0: case 1: case 2:
+ case 6: case 8:
+ break;
+ default:
+ return (PCMDMX_UNABLE_TO_SET_PARAM);
+ }
+ if (self == NULL)
+ return (PCMDMX_INVALID_HANDLE);
+ if ((int)value > 0) {
+ self->numOutputChannels = (int)value;
+ self->applyProcessing = 1;
+ } else {
+ self->numOutputChannels = 0;
+ self->applyProcessing = 0;
+ }
+ break;
+
+ case DUAL_CHANNEL_DOWNMIX_MODE:
+ switch ((DUAL_CHANNEL_MODE)value) {
+ case STEREO_MODE:
+ case CH1_MODE:
+ case CH2_MODE:
+ case MIXED_MODE:
+ break;
+ default:
+ return (PCMDMX_UNABLE_TO_SET_PARAM);
+ }
+ if (self == NULL)
+ return (PCMDMX_INVALID_HANDLE);
+ self->dualChannelMode = (DUAL_CHANNEL_MODE)value;
+ self->applyProcessing = 1;
+ break;
+
+ default:
+ return (PCMDMX_UNKNOWN_PARAM);
+ }
+
+ return (PCMDMX_OK);
+}
+
+
+/** Read the ancillary data transported in DSEs of DVB streams with MPEG-4 content
+ * @param [in] Handle of PCM downmix module instance.
+ * @param [in] Pointer to ancillary data buffer.
+ * @param [in] Size of ancillary data.
+ * @param [in] Flag indicating wheter the DVB ancillary data is from an MPEG-1/2 or an MPEG-4 stream.
+ * @returns Returns an error code.
+ **/
+PCMDMX_ERROR pcmDmx_ReadDvbAncData (
+ HANDLE_PCM_DOWNMIX self,
+ UCHAR *pAncDataBuf,
+ UINT ancDataBytes,
+ int isMpeg2
+ )
+{
+ DVB_MIXDOWN_LEVELS *pDownmixLevels = &self->dvbMixDownLevels[0];
+
+ int offset = (isMpeg2) ? 2 : 0;
+ UCHAR ancDataStatus;
+
+ if (self == NULL) { return (PCMDMX_INVALID_HANDLE); }
+
+ /* sanity checks */
+ if (pAncDataBuf == NULL || ancDataBytes < (UCHAR)(3+offset)) {
+ return (PCMDMX_CORRUPT_ANC_DATA);
+ }
+
+ /* check sync word */
+ if (pAncDataBuf[offset] != ANC_DATA_SYNC_BYTE) {
+ return (PCMDMX_CORRUPT_ANC_DATA);
+ }
+
+ offset += 2;
+ ancDataStatus = pAncDataBuf[offset++];
+
+ if (isMpeg2) {
+ /* skip advanced_dynamic_range_control */
+ if (ancDataStatus & 0x80) offset += 3;
+ /* skip dialog_normalization */
+ if (ancDataStatus & 0x40) offset += 1;
+ /* skip reproduction_level */
+ if (ancDataStatus & 0x20) offset += 1;
+ }
+ else {
+ /* check reserved bits */
+ if (ancDataStatus & 0xE8) { return (PCMDMX_CORRUPT_ANC_DATA); }
+ }
+
+ /* downmix_levels_MPEGX */
+ if (ancDataStatus & 0x10)
+ {
+ int foundNewData = 0;
+ UCHAR downmixData = pAncDataBuf[offset++];
+
+ if (downmixData & 0x80) { /* center_mix_level_on */
+ pDownmixLevels->centerMixLevelValue =
+ dvbDownmixFactors[(downmixData >> 4) & 0x07];
+ foundNewData = 1;
+ } else {
+ pDownmixLevels->centerMixLevelValue = dvbDownmixFactors[0];
+ if (downmixData & 0x70) { return (PCMDMX_CORRUPT_ANC_DATA); }
+ }
+
+ if (downmixData & 0x08) { /* surround_mix_level_on */
+ pDownmixLevels->surroundMixLevelValue =
+ dvbDownmixFactors[downmixData & 0x07];
+ foundNewData = 1;
+ } else {
+ pDownmixLevels->surroundMixLevelValue = dvbDownmixFactors[0];
+ if (downmixData & 0x07) { return (PCMDMX_CORRUPT_ANC_DATA); }
+ }
+
+ pDownmixLevels->mixLevelsAvail = foundNewData;
+ }
+
+ /* Reset expiry counter */
+ self->expiryCount = 0;
+
+ return (PCMDMX_OK);
+}
+
+/** Set the matrix mixdown information extracted from the PCE of an AAC bitstream.
+ * Note: Call only if matrix_mixdown_idx_present is true.
+ * @param [in] Handle of PCM downmix module instance.
+ * @param [in] The 2 bit matrix mixdown index extracted from PCE.
+ * @param [in] The pseudo surround enable flag extracted from PCE.
+ * @returns Returns an error code.
+ **/
+PCMDMX_ERROR pcmDmx_SetMatrixMixdownFromPce (
+ HANDLE_PCM_DOWNMIX self,
+ int matrixMixdownPresent,
+ int matrixMixdownIdx,
+ int pseudoSurroundEnable
+ )
+{
+ MPEG_MIXDOWN_INFO *pMpegMixDownInfo;
+
+ if (self == NULL) {
+ return (PCMDMX_INVALID_HANDLE);
+ }
+
+ pMpegMixDownInfo = &self->mpegMixDownInfo[0];
+
+ if (matrixMixdownPresent) {
+ pMpegMixDownInfo->matrixMixdownIdx = matrixMixdownIdx & 0x03;
+ pMpegMixDownInfo->pseudoSurroundEnable = pseudoSurroundEnable;
+ }
+
+ pMpegMixDownInfo->mixdownAvailable = matrixMixdownPresent;
+ /* Reset expiry counter */
+ self->expiryCount = 0;
+
+ return (PCMDMX_OK);
+}
+
+
+/** Apply down or up mixing.
+ * @param [in] Handle of PCM downmix module instance.
+ * @param [inout] Pointer to time buffer. Depending on interface configuration, the content of pTimeData is ignored,
+ * and the internal QMF buffer will be used as input data source. Otherwise, the MPEG Surround processing is
+ * applied to the timesignal pTimeData. For both variants, the resulting MPEG Surround signal is written into pTimeData.
+ * @param [in] Pointer where the amount of output samples is returned into.
+ * @param [inout] Pointer where the amount of output channels is returned into.
+ * @param [in] Flag which indicates if output time data are writtern interleaved or as subsequent blocks.
+ * @param [inout] Array where the corresponding channel type for each output audio channel is stored into.
+ * @param [inout] Array where the corresponding channel type index for each output audio channel is stored into.
+ * @param [in] Array containing the output channel mapping to be used (From MPEG PCE ordering to whatever is required).
+ * @returns Returns an error code.
+ **/
+PCMDMX_ERROR pcmDmx_ApplyFrame (
+ HANDLE_PCM_DOWNMIX self,
+ INT_PCM *pPcmBuf,
+ UINT frameSize,
+ INT *nChannels,
+
+ int fInterleaved,
+ AUDIO_CHANNEL_TYPE channelType[],
+ UCHAR channelIndices[],
+ const UCHAR channelMapping[][8]
+ )
+{
+ PCMDMX_ERROR errorStatus = PCMDMX_OK;
+ DUAL_CHANNEL_MODE dualChannelMode;
+ PCM_DMX_CHANNEL_MODE inChMode;
+ int numOutChannels;
+ int numInChannels = *nChannels;
+ int slot;
+ UCHAR inOffsetTable[PCM_DMX_MAX_CHANNELS];
+
+ MPEG_MIXDOWN_INFO mpegMixDownInfo;
+ DVB_MIXDOWN_LEVELS dvbMixDownLevels;
+
+ if (self == NULL) { return (PCMDMX_INVALID_HANDLE); }
+
+ if ( (self->expiryFrame > 0)
+ && (++self->expiryCount > self->expiryFrame) )
+ { /* The metadata read from bitstream is too old. */
+ errorStatus = pcmDmx_Reset(self, PCMDMX_RESET_BS_DATA);
+ }
+
+ FDKmemcpy(&mpegMixDownInfo, &self->mpegMixDownInfo[self->frameDelay], sizeof(MPEG_MIXDOWN_INFO));
+ /* Maintain delay line */
+ for (slot = self->frameDelay; slot > 0; slot -= 1) {
+ FDKmemcpy(&self->mpegMixDownInfo[slot], &self->mpegMixDownInfo[slot-1], sizeof(MPEG_MIXDOWN_INFO));
+ }
+ FDKmemcpy(&dvbMixDownLevels, &self->dvbMixDownLevels[self->frameDelay], sizeof(DVB_MIXDOWN_LEVELS));
+ /* Maintain delay line */
+ for (slot = self->frameDelay; slot > 0; slot -= 1) {
+ FDKmemcpy(&self->dvbMixDownLevels[slot], &self->dvbMixDownLevels[slot-1], sizeof(DVB_MIXDOWN_LEVELS));
+ }
+
+ if (self->applyProcessing == 0) { return (errorStatus); }
+
+ if (pPcmBuf == NULL) { return (PCMDMX_INVALID_ARGUMENT); }
+ if (frameSize == 0) { return (PCMDMX_INVALID_ARGUMENT); }
+ if (numInChannels == 0) { return (PCMDMX_INVALID_ARGUMENT); }
+
+ if (self->numOutputChannels <= 0) {
+ numOutChannels = numInChannels;
+ } else {
+ numOutChannels = self->numOutputChannels;
+ }
+ dualChannelMode = self->dualChannelMode;
+
+ /* Analyse input channel configuration and get channel offset
+ * table that can be accessed with the fixed channel labels. */
+ inChMode = getChannelMode(
+ numInChannels,
+ channelType,
+ channelIndices,
+ channelMapping[numInChannels],
+ inOffsetTable
+ );
+ if (inChMode == CH_MODE_UNDEFINED) {
+ /* We don't need to restore because the channel
+ configuration has not been changed. Just exit. */
+ return (PCMDMX_INVALID_CH_CONFIG);
+ }
+
+ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ if ( numInChannels > numOutChannels )
+ { /* Apply downmix */
+ INT_PCM *pInCF, *pInLF, *pInRF, *pInLO, *pInRO, *pInLR, *pInRR, *pOutL, *pOutR;
+ FIXP_SGL flev, clev, slev;
+
+ UINT sample;
+ int inStride, outStride, offset;
+ int useGuidedDownMix = 0;
+ UCHAR outOffsetTable[PCM_DMX_MAX_CHANNELS];
+
+ /* Set I/O strides and offsets */
+ if (fInterleaved) {
+ inStride = numInChannels;
+ outStride = TWO_CHANNEL; /* The output of STAGE ONE is always STEREO !!!
+ STAGE TWO creates a downmix to mono if required. */
+ offset = 1; /* Channel specific offset factor */
+ } else {
+ inStride = 1;
+ outStride = 1;
+ offset = frameSize; /* Channel specific offset factor */
+ }
+
+ /* Get channel description and channel mapping for this
+ * stages number of output channels (always STEREO). */
+ getChannelDescription(
+ CH_MODE_2_0_0_0,
+ channelMapping,
+ channelType,
+ channelIndices,
+ outOffsetTable
+ );
+ /* Now there is no way back because we modified the channel configuration! */
+
+ /* Set channel pointer for input */
+ pInCF = &pPcmBuf[inOffsetTable[CENTER_FRONT_CHANNEL]*offset];
+ pInLF = &pPcmBuf[inOffsetTable[LEFT_FRONT_CHANNEL]*offset];
+ pInRF = &pPcmBuf[inOffsetTable[RIGHT_FRONT_CHANNEL]*offset];
+ pInLO = &pPcmBuf[inOffsetTable[LEFT_OUTSIDE_CHANNEL]*offset];
+ pInRO = &pPcmBuf[inOffsetTable[RIGHT_OUTSIDE_CHANNEL]*offset];
+ pInLR = &pPcmBuf[inOffsetTable[LEFT_REAR_CHANNEL]*offset];
+ pInRR = &pPcmBuf[inOffsetTable[RIGHT_REAR_CHANNEL]*offset];
+
+ /* Set channel pointer for output
+ Caution: Different channel mapping compared to input */
+ pOutL = &pPcmBuf[outOffsetTable[LEFT_FRONT_CHANNEL]*offset]; /* LEFT_FRONT_CHANNEL */
+ pOutR = &pPcmBuf[outOffsetTable[RIGHT_FRONT_CHANNEL]*offset]; /* RIGHT_FRONT_CHANNEL */
+
+ /* Set downmix levels: */
+ flev = ATTENUATION_FACTOR_1; /* 0.707 */
+ clev = ATTENUATION_FACTOR_1; /* 0.707 */
+ slev = ATTENUATION_FACTOR_1; /* 0.707 */
+
+ if ( dvbMixDownLevels.mixLevelsAvail ) {
+ clev = dvbMixDownLevels.centerMixLevelValue;
+ slev = dvbMixDownLevels.surroundMixLevelValue;
+ useGuidedDownMix = 1;
+ }
+
+ /* FIRST STAGE:
+ Always downmix to 2 channel output: */
+ switch ( inChMode )
+ {
+ case CH_MODE_2_0_0_0:
+ case CH_MODE_2_0_0_1:
+ /* 2/0 input: */
+ switch (dualChannelMode)
+ {
+ case CH1_MODE: /* L' = 0.707 * Ch1; R' = 0.707 * Ch1 */
+ for (sample = 0; sample < frameSize; sample++) {
+ *pOutL = *pOutR =
+ (INT_PCM)SATURATE_RIGHT_SHIFT(fMult((FIXP_PCM)*pInLF, flev), DFRACT_BITS-SAMPLE_BITS, SAMPLE_BITS);
+
+ pInLF += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ break;
+
+ case CH2_MODE: /* L' = 0.707 * Ch2; R' = 0.707 * Ch2 */
+ for (sample = 0; sample < frameSize; sample++) {
+ *pOutL = *pOutR =
+ (INT_PCM)SATURATE_RIGHT_SHIFT(fMult((FIXP_PCM)*pInRF, flev), DFRACT_BITS-SAMPLE_BITS, SAMPLE_BITS);
+
+ pInRF += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ break;
+ case MIXED_MODE: /* L' = 0.5*Ch1 + 0.5*Ch2; R' = 0.5*Ch1 + 0.5*Ch2 */
+ for (sample = 0; sample < frameSize; sample++) {
+ *pOutL = *pOutR = (*pInLF >> 1) + (*pInRF >> 1);
+
+ pInLF += inStride; pInRF += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ break;
+ default:
+ case STEREO_MODE:
+ /* nothing to do */
+ break;
+ }
+ break;
+
+ case CH_MODE_3_0_0_0:
+ /* 3/0 input: L' = L + 0.707*C; R' = R + 0.707*C; */
+ for (sample = 0; sample < frameSize; sample++)
+ {
+ FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev);
+#if (SAMPLE_BITS == 32)
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>1)+tCF, 1, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>1)+tCF, 1, SAMPLE_BITS);
+#else
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>1)+tCF, DFRACT_BITS-SAMPLE_BITS-1, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>1)+tCF, DFRACT_BITS-SAMPLE_BITS-1, SAMPLE_BITS);
+#endif
+ pInLF += inStride; pInRF += inStride; pInCF += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ break;
+
+ /* 2/1 input: not supported!
+ case CH_MODE_2_0_1_0: */
+
+ case CH_MODE_3_0_1_0:
+ if (useGuidedDownMix) {
+ /* 3/1 input: L' = L + clev*C + 0.707*slev*S; R' = R + clev*C + 0.707*slev*S; */
+ slev = FX_DBL2FX_SGL(fMult(flev, slev)); /* 0.707*slef */
+
+ for (sample = 0; sample < frameSize; sample++)
+ {
+ FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev) >> 1;
+ FIXP_DBL tLR = fMultDiv2((FIXP_PCM)*pInLR, slev) >> 1;
+#if (SAMPLE_BITS == 32)
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF+tLR, 2, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tLR, 2, SAMPLE_BITS);
+#else
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF-tLR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tLR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS);
+#endif
+ pInLF += inStride; pInRF += inStride; pInCF += inStride; pInLR += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ } else {
+ /* 3/1 input: L' = L + 0.707*C - 0.707*S; R' = R + 0.707*C + 0.707*S */
+ for (sample = 0; sample < frameSize; sample++)
+ {
+ FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev) >> 1;
+ FIXP_DBL tLR = fMultDiv2((FIXP_PCM)*pInLR, slev) >> 1;
+#if (SAMPLE_BITS == 32)
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF-tLR, 2, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tLR, 2, SAMPLE_BITS);
+#else
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF-tLR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tLR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS);
+#endif
+ pInLF += inStride; pInRF += inStride; pInCF += inStride; pInLR += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ }
+ break;
+
+ /* 2/2 input: not supported!
+ case CH_MODE_2_0_2_0: */
+
+ case CH_MODE_3_0_2_0: /* 5.0ch input */
+ case CH_MODE_3_0_2_1: /* 5.1ch input */
+ if (useGuidedDownMix) {
+ /* 3/2 input: L' = L + clev*C + slev*Ls; R' = R + clev*C + slev*Rs; */
+ for (sample = 0; sample < frameSize; sample++)
+ {
+ FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev) >> 1;
+ FIXP_DBL tLR = fMultDiv2((FIXP_PCM)*pInLR, slev) >> 1;
+ FIXP_DBL tRR = fMultDiv2((FIXP_PCM)*pInRR, slev) >> 1;
+#if (SAMPLE_BITS == 32)
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF+tLR, 2, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tRR, 2, SAMPLE_BITS);
+#else
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>2)+tCF+tLR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>2)+tCF+tRR, DFRACT_BITS-SAMPLE_BITS-2, SAMPLE_BITS);
+#endif
+ pInLF += inStride; pInRF += inStride; pInCF += inStride; pInLR += inStride; pInRR += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ }
+ else if (mpegMixDownInfo.mixdownAvailable) {
+ /* 3/2 input: L' = (1.707+A)^-1 * [L+0.707*C+A*Ls]; R'= (1.707+A)^-1 * [R+0.707*C+A*Rs]; */
+ FIXP_SGL mtrxMixDwnCoef = mpegMixDownIdx2Coef[mpegMixDownInfo.matrixMixdownIdx];
+ FIXP_SGL mtrxMixDwnPreFact = mpegMixDownIdx2PreFact[mpegMixDownInfo.matrixMixdownIdx];
+ clev = FX_DBL2FX_SGL(fMult(mtrxMixDwnPreFact, flev /* 0.707 */));
+ flev = mtrxMixDwnPreFact;
+ slev = FX_DBL2FX_SGL(fMult(mtrxMixDwnPreFact, mtrxMixDwnCoef));
+
+ for (sample = 0; sample < frameSize; sample++)
+ {
+ FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev);
+ FIXP_DBL tLF = fMultDiv2((FIXP_PCM)*pInLF, flev);
+ FIXP_DBL tRF = fMultDiv2((FIXP_PCM)*pInRF, flev);
+ FIXP_DBL tLR = fMultDiv2((FIXP_PCM)*pInLR, slev);
+ FIXP_DBL tRR = fMultDiv2((FIXP_PCM)*pInRR, slev);
+
+#if (SAMPLE_BITS == 32)
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT(tLF+tCF+tLR, 1, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT(tRF+tCF+tRR, 1, SAMPLE_BITS);
+#else
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT(tLF+tCF+tLR, DFRACT_BITS-SAMPLE_BITS-1, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT(tRF+tCF+tRR, DFRACT_BITS-SAMPLE_BITS-1, SAMPLE_BITS);
+#endif
+
+ pInLF += inStride; pInRF += inStride; pInCF += inStride; pInLR += inStride; pInRR += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ }
+ else {
+ /* 3/2 input: L' = L + 0.707*C - 0.707*Ls - 0.707*Rs; R' = R + 0.707*C + 0.707*Ls + 0.707*Rs */
+ for (sample = 0; sample < frameSize; sample++)
+ {
+ FIXP_DBL tCF = fMultDiv2((FIXP_PCM)*pInCF, clev) >> 2;
+ FIXP_DBL tLR = fMultDiv2((FIXP_PCM)*pInLR, slev) >> 2;
+ FIXP_DBL tRR = fMultDiv2((FIXP_PCM)*pInRR, slev) >> 2;
+#if (SAMPLE_BITS == 32)
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>3)+tCF-tLR-tRR, 3, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_LEFT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>3)+tCF+tLR+tRR, 3, SAMPLE_BITS);
+#else
+ /* left channel */
+ *pOutL = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInLF)>>3)+tCF-tLR-tRR, DFRACT_BITS-SAMPLE_BITS-3, SAMPLE_BITS);
+ /* right channel */
+ *pOutR = (INT_PCM)SATURATE_RIGHT_SHIFT((FX_PCM2FX_DBL((FIXP_PCM)*pInRF)>>3)+tCF+tLR+tRR, DFRACT_BITS-SAMPLE_BITS-3, SAMPLE_BITS);
+#endif
+ pInLF += inStride; pInRF += inStride; pInCF += inStride; pInLR += inStride; pInRR += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ }
+ break;
+
+ default:
+ errorStatus = PCMDMX_INVALID_MODE;
+ break;
+ }
+
+ /* SECOND STAGE:
+ If desired create a mono donwmix:
+ Note: Input are always two channels! */
+ if (numOutChannels == 1)
+ {
+ INT_PCM *pOutC;
+ FIXP_SGL mlev;
+
+ if (useGuidedDownMix) mlev = FL2FXCONST_SGL(1.0f); else mlev = flev;
+
+ /* Output of STAGE ONE = Input of STAGE TWO */
+ FDKmemcpy(inOffsetTable, outOffsetTable, PCM_DMX_MAX_CHANNELS*sizeof(UCHAR));
+
+ /* Set I/O strides and offsets */
+ inStride = outStride; /* output from STAGE ONE */
+ outStride = numOutChannels; /* final output */
+
+ /* Get channel description and channel mapping for this
+ * stages number of output channels (always MONO). */
+ getChannelDescription(
+ CH_MODE_1_0_0_0,
+ channelMapping,
+ channelType,
+ channelIndices,
+ outOffsetTable
+ );
+
+ /* Set input channel pointer. */
+ pInLF = &pPcmBuf[inOffsetTable[LEFT_FRONT_CHANNEL]*offset];
+ pInRF = &pPcmBuf[inOffsetTable[RIGHT_FRONT_CHANNEL]*offset];
+
+ /* Set output channel pointer */
+ pOutC = &pPcmBuf[outOffsetTable[CENTER_FRONT_CHANNEL]*offset];
+
+ /* C' = 0.707*L + 0.707*R */
+ for (sample = 0; sample < frameSize; sample++) {
+#if (SAMPLE_BITS == 32)
+ *pOutC =
+ (INT_PCM)SATURATE_LEFT_SHIFT(fMultDiv2((FIXP_PCM)*pInLF,mlev)+fMultDiv2((FIXP_PCM)*pInRF,mlev), 1, SAMPLE_BITS);
+#else
+ *pOutC =
+ (INT_PCM)SATURATE_RIGHT_SHIFT(fMultDiv2((FIXP_PCM)*pInLF,mlev)+fMultDiv2((FIXP_PCM)*pInRF,mlev), DFRACT_BITS-SAMPLE_BITS-1, SAMPLE_BITS);
+#endif
+
+ pInLF += inStride; pInRF += inStride;
+ pOutC += 1;
+ }
+ /* Finished STAGE TWO */
+ }
+
+ /* Update the number of output channels */
+ *nChannels = self->numOutputChannels;
+
+ } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ else
+ if ( numInChannels == numOutChannels )
+ {
+ /* Don't need to change the channel description here */
+
+ switch (numInChannels)
+ {
+ case 2:
+ { /* Set up channel pointer */
+ INT_PCM *pInLF, *pInRF, *pOutL, *pOutR;
+ FIXP_SGL flev;
+
+ UINT sample;
+ int inStride, outStride, offset;
+
+ if (fInterleaved) {
+ inStride = numInChannels;
+ outStride = 2; /* fixed !!! (below stereo is donwmixed to mono if required */
+ offset = 1; /* Channel specific offset factor */
+ } else {
+ inStride = 1;
+ outStride = 1;
+ offset = frameSize; /* Channel specific offset factor */
+ }
+
+ /* Set input channel pointer */
+ pInLF = &pPcmBuf[inOffsetTable[LEFT_FRONT_CHANNEL]*offset];
+ pInRF = &pPcmBuf[inOffsetTable[RIGHT_FRONT_CHANNEL]*offset];
+
+ /* Set output channel pointer (same as input) */
+ pOutL = pInLF;
+ pOutR = pInRF;
+
+ /* Set downmix levels: */
+ flev = ATTENUATION_FACTOR_1; /* 0.707 */
+ /* 2/0 input: */
+ switch (dualChannelMode)
+ {
+ case CH1_MODE: /* L' = 0.707 * Ch1; R' = 0.707 * Ch1 */
+ for (sample = 0; sample < frameSize; sample++) {
+ *pOutL = *pOutR =
+ (INT_PCM)SATURATE_RIGHT_SHIFT(fMult((FIXP_PCM)*pInLF, flev), DFRACT_BITS-SAMPLE_BITS, SAMPLE_BITS);
+
+ pInLF += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ break;
+ case CH2_MODE: /* L' = 0.707 * Ch2; R' = 0.707 * Ch2 */
+ for (sample = 0; sample < frameSize; sample++) {
+ *pOutL = *pOutR =
+ (INT_PCM)SATURATE_RIGHT_SHIFT(fMult((FIXP_PCM)*pInRF, flev), DFRACT_BITS-SAMPLE_BITS, SAMPLE_BITS);
+
+ pInRF += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ break;
+ case MIXED_MODE: /* L' = 0.5*Ch1 + 0.5*Ch2; R' = 0.5*Ch1 + 0.5*Ch2 */
+ for (sample = 0; sample < frameSize; sample++) {
+ *pOutL = *pOutR = (*pInLF >> 1) + (*pInRF >> 1);
+
+ pInLF += inStride; pInRF += inStride;
+ pOutL += outStride; pOutR += outStride;
+ }
+ break;
+ default:
+ case STEREO_MODE:
+ /* nothing to do */
+ break;
+ }
+ }
+ break;
+
+ default:
+ /* nothing to do */
+ break;
+ }
+ }
+
+ return (errorStatus);
+}
+
+
+/** Close an instance of the PCM downmix module.
+ * @param [inout] Pointer to a buffer containing the handle of the instance.
+ * @returns Returns an error code.
+ **/
+PCMDMX_ERROR pcmDmx_Close (
+ HANDLE_PCM_DOWNMIX *pSelf
+ )
+{
+ if (pSelf == NULL) {
+ return (PCMDMX_INVALID_HANDLE);
+ }
+
+ FreePcmDmxInstance( pSelf );
+ *pSelf = NULL;
+
+ return (PCMDMX_OK);
+}
+
+
+/** Get library info for this module.
+ * @param [out] Pointer to an allocated LIB_INFO structure.
+ * @returns Returns an error code.
+ */
+PCMDMX_ERROR pcmDmx_GetLibInfo( LIB_INFO *info )
+{
+ int i;
+
+ if (info == NULL) {
+ return PCMDMX_INVALID_ARGUMENT;
+ }
+
+ /* Search for next free tab */
+ for (i = 0; i < FDK_MODULE_LAST; i++) {
+ if (info[i].module_id == FDK_NONE) break;
+ }
+ if (i == FDK_MODULE_LAST) {
+ return PCMDMX_UNKNOWN;
+ }
+ info += i;
+
+ /* Add the library info */
+ info->module_id = FDK_PCMDMX;
+ info->version = LIB_VERSION(PCMDMX_LIB_VL0, PCMDMX_LIB_VL1, PCMDMX_LIB_VL2);
+ LIB_VERSION_STRING(info);
+ info->build_date = PCMDMX_LIB_BUILD_DATE;
+ info->build_time = PCMDMX_LIB_BUILD_TIME;
+ info->title = PCMDMX_LIB_TITLE;
+
+ /* Set flags */
+ info->flags = 0
+ | CAPF_DMX_BLIND /* At least blind downmixing is possible */
+ | CAPF_DMX_PCE /* Guided downmix with data from MPEG-2/4 Program Config Elements (PCE). */
+ | CAPF_DMX_DVB /* Guided downmix with data from DVB ancillary data fields. */
+ ;
+
+ return PCMDMX_OK;
+}
+
+
+