Home | History | Annotate | Download | only in downmix
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "EffectDownmix"
     18 //#define LOG_NDEBUG 0
     19 #include <log/log.h>
     20 #include <inttypes.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <stdbool.h>
     24 #include "EffectDownmix.h"
     25 
     26 // Do not submit with DOWNMIX_TEST_CHANNEL_INDEX defined, strictly for testing
     27 //#define DOWNMIX_TEST_CHANNEL_INDEX 0
     28 // Do not submit with DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER defined, strictly for testing
     29 //#define DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER 0
     30 
     31 #define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896
     32 
     33 // subset of possible audio_channel_mask_t values, and AUDIO_CHANNEL_OUT_* renamed to CHANNEL_MASK_*
     34 typedef enum {
     35     CHANNEL_MASK_QUAD_BACK = AUDIO_CHANNEL_OUT_QUAD_BACK,
     36     CHANNEL_MASK_QUAD_SIDE = AUDIO_CHANNEL_OUT_QUAD_SIDE,
     37     CHANNEL_MASK_5POINT1_BACK = AUDIO_CHANNEL_OUT_5POINT1_BACK,
     38     CHANNEL_MASK_5POINT1_SIDE = AUDIO_CHANNEL_OUT_5POINT1_SIDE,
     39     CHANNEL_MASK_7POINT1 = AUDIO_CHANNEL_OUT_7POINT1,
     40 } downmix_input_channel_mask_t;
     41 
     42 // effect_handle_t interface implementation for downmix effect
     43 const struct effect_interface_s gDownmixInterface = {
     44         Downmix_Process,
     45         Downmix_Command,
     46         Downmix_GetDescriptor,
     47         NULL /* no process_reverse function, no reference stream needed */
     48 };
     49 
     50 // This is the only symbol that needs to be exported
     51 __attribute__ ((visibility ("default")))
     52 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
     53     .tag = AUDIO_EFFECT_LIBRARY_TAG,
     54     .version = EFFECT_LIBRARY_API_VERSION,
     55     .name = "Downmix Library",
     56     .implementor = "The Android Open Source Project",
     57     .create_effect = DownmixLib_Create,
     58     .release_effect = DownmixLib_Release,
     59     .get_descriptor = DownmixLib_GetDescriptor,
     60 };
     61 
     62 
     63 // AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
     64 static const effect_descriptor_t gDownmixDescriptor = {
     65         EFFECT_UIID_DOWNMIX__, //type
     66         {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
     67         EFFECT_CONTROL_API_VERSION,
     68         EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
     69         0, //FIXME what value should be reported? // cpu load
     70         0, //FIXME what value should be reported? // memory usage
     71         "Multichannel Downmix To Stereo", // human readable effect name
     72         "The Android Open Source Project" // human readable effect implementor name
     73 };
     74 
     75 // gDescriptors contains pointers to all defined effect descriptor in this library
     76 static const effect_descriptor_t * const gDescriptors[] = {
     77         &gDownmixDescriptor
     78 };
     79 
     80 // number of effects in this library
     81 const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
     82 
     83 
     84 /*----------------------------------------------------------------------------
     85  * Test code
     86  *--------------------------------------------------------------------------*/
     87 #ifdef DOWNMIX_TEST_CHANNEL_INDEX
     88 // strictly for testing, logs the indices of the channels for a given mask,
     89 // uses the same code as Downmix_foldGeneric()
     90 void Downmix_testIndexComputation(uint32_t mask) {
     91     ALOGI("Testing index computation for 0x%" PRIx32 ":", mask);
     92     // check against unsupported channels
     93     if (mask & kUnsupported) {
     94         ALOGE("Unsupported channels (top or front left/right of center)");
     95         return;
     96     }
     97     // verify has FL/FR
     98     if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
     99         ALOGE("Front channels must be present");
    100         return;
    101     }
    102     // verify uses SIDE as a pair (ok if not using SIDE at all)
    103     bool hasSides = false;
    104     if ((mask & kSides) != 0) {
    105         if ((mask & kSides) != kSides) {
    106             ALOGE("Side channels must be used as a pair");
    107             return;
    108         }
    109         hasSides = true;
    110     }
    111     // verify uses BACK as a pair (ok if not using BACK at all)
    112     bool hasBacks = false;
    113     if ((mask & kBacks) != 0) {
    114         if ((mask & kBacks) != kBacks) {
    115             ALOGE("Back channels must be used as a pair");
    116             return;
    117         }
    118         hasBacks = true;
    119     }
    120 
    121     const int numChan = audio_channel_count_from_out_mask(mask);
    122     const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
    123     const bool hasLFE =
    124             ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
    125     const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
    126     // compute at what index each channel is: samples will be in the following order:
    127     //   FL FR FC LFE BL BR BC SL SR
    128     // when a channel is not present, its index is set to the same as the index of the preceding
    129     // channel
    130     const int indexFC  = hasFC    ? 2            : 1;        // front center
    131     const int indexLFE = hasLFE   ? indexFC + 1  : indexFC;  // low frequency
    132     const int indexBL  = hasBacks ? indexLFE + 1 : indexLFE; // back left
    133     const int indexBR  = hasBacks ? indexBL + 1  : indexBL;  // back right
    134     const int indexBC  = hasBC    ? indexBR + 1  : indexBR;  // back center
    135     const int indexSL  = hasSides ? indexBC + 1  : indexBC;  // side left
    136     const int indexSR  = hasSides ? indexSL + 1  : indexSL;  // side right
    137 
    138     ALOGI("  FL FR FC LFE BL BR BC SL SR");
    139     ALOGI("   %d  %d  %d   %d  %d  %d  %d  %d  %d",
    140             0, 1, indexFC, indexLFE, indexBL, indexBR, indexBC, indexSL, indexSR);
    141 }
    142 #endif
    143 
    144 
    145 /*----------------------------------------------------------------------------
    146  * Effect API implementation
    147  *--------------------------------------------------------------------------*/
    148 
    149 /*--- Effect Library Interface Implementation ---*/
    150 
    151 int32_t DownmixLib_Create(const effect_uuid_t *uuid,
    152         int32_t sessionId __unused,
    153         int32_t ioId __unused,
    154         effect_handle_t *pHandle) {
    155     int ret;
    156     int i;
    157     downmix_module_t *module;
    158     const effect_descriptor_t *desc;
    159 
    160     ALOGV("DownmixLib_Create()");
    161 
    162 #ifdef DOWNMIX_TEST_CHANNEL_INDEX
    163     // should work (won't log an error)
    164     ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should work:");
    165     Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
    166                     AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_CENTER);
    167     Downmix_testIndexComputation(CHANNEL_MASK_QUAD_SIDE | CHANNEL_MASK_QUAD_BACK);
    168     Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_SIDE | AUDIO_CHANNEL_OUT_BACK_CENTER);
    169     Downmix_testIndexComputation(CHANNEL_MASK_5POINT1_BACK | AUDIO_CHANNEL_OUT_BACK_CENTER);
    170     // shouldn't work (will log an error, won't display channel indices)
    171     ALOGI("DOWNMIX_TEST_CHANNEL_INDEX: should NOT work:");
    172     Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
    173                         AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_BACK_LEFT);
    174     Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT | AUDIO_CHANNEL_OUT_FRONT_RIGHT |
    175                             AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_SIDE_LEFT);
    176     Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
    177                         AUDIO_CHANNEL_OUT_BACK_LEFT | AUDIO_CHANNEL_OUT_BACK_RIGHT);
    178     Downmix_testIndexComputation(AUDIO_CHANNEL_OUT_FRONT_LEFT |
    179                             AUDIO_CHANNEL_OUT_SIDE_LEFT | AUDIO_CHANNEL_OUT_SIDE_RIGHT);
    180 #endif
    181 
    182     if (pHandle == NULL || uuid == NULL) {
    183         return -EINVAL;
    184     }
    185 
    186     for (i = 0 ; i < kNbEffects ; i++) {
    187         desc = gDescriptors[i];
    188         if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
    189             break;
    190         }
    191     }
    192 
    193     if (i == kNbEffects) {
    194         return -ENOENT;
    195     }
    196 
    197     module = malloc(sizeof(downmix_module_t));
    198 
    199     module->itfe = &gDownmixInterface;
    200 
    201     module->context.state = DOWNMIX_STATE_UNINITIALIZED;
    202 
    203     ret = Downmix_Init(module);
    204     if (ret < 0) {
    205         ALOGW("DownmixLib_Create() init failed");
    206         free(module);
    207         return ret;
    208     }
    209 
    210     *pHandle = (effect_handle_t) module;
    211 
    212     ALOGV("DownmixLib_Create() %p , size %zu", module, sizeof(downmix_module_t));
    213 
    214     return 0;
    215 }
    216 
    217 
    218 int32_t DownmixLib_Release(effect_handle_t handle) {
    219     downmix_module_t *pDwmModule = (downmix_module_t *)handle;
    220 
    221     ALOGV("DownmixLib_Release() %p", handle);
    222     if (handle == NULL) {
    223         return -EINVAL;
    224     }
    225 
    226     pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
    227 
    228     free(pDwmModule);
    229     return 0;
    230 }
    231 
    232 
    233 int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
    234     ALOGV("DownmixLib_GetDescriptor()");
    235     int i;
    236 
    237     if (pDescriptor == NULL || uuid == NULL){
    238         ALOGE("DownmixLib_Create() called with NULL pointer");
    239         return -EINVAL;
    240     }
    241     ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
    242     for (i = 0; i < kNbEffects; i++) {
    243         ALOGV("DownmixLib_GetDescriptor() i=%d", i);
    244         if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
    245             memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
    246             ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %" PRIx32,
    247                  i, gDescriptors[i]->uuid.timeLow);
    248             return 0;
    249         }
    250     }
    251 
    252     return -EINVAL;
    253 }
    254 
    255 
    256 /*--- Effect Control Interface Implementation ---*/
    257 
    258 static int Downmix_Process(effect_handle_t self,
    259         audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
    260 
    261     downmix_object_t *pDownmixer;
    262     int16_t *pSrc, *pDst;
    263     downmix_module_t *pDwmModule = (downmix_module_t *)self;
    264 
    265     if (pDwmModule == NULL) {
    266         return -EINVAL;
    267     }
    268 
    269     if (inBuffer == NULL || inBuffer->raw == NULL ||
    270         outBuffer == NULL || outBuffer->raw == NULL ||
    271         inBuffer->frameCount != outBuffer->frameCount) {
    272         return -EINVAL;
    273     }
    274 
    275     pDownmixer = (downmix_object_t*) &pDwmModule->context;
    276 
    277     if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
    278         ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
    279         return -EINVAL;
    280     } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
    281         ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
    282         return -ENODATA;
    283     }
    284 
    285     pSrc = inBuffer->s16;
    286     pDst = outBuffer->s16;
    287     size_t numFrames = outBuffer->frameCount;
    288 
    289     const bool accumulate =
    290             (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
    291     const uint32_t downmixInputChannelMask = pDwmModule->config.inputCfg.channels;
    292 
    293     switch(pDownmixer->type) {
    294 
    295       case DOWNMIX_TYPE_STRIP:
    296           if (accumulate) {
    297               while (numFrames) {
    298                   pDst[0] = clamp16(pDst[0] + pSrc[0]);
    299                   pDst[1] = clamp16(pDst[1] + pSrc[1]);
    300                   pSrc += pDownmixer->input_channel_count;
    301                   pDst += 2;
    302                   numFrames--;
    303               }
    304           } else {
    305               while (numFrames) {
    306                   pDst[0] = pSrc[0];
    307                   pDst[1] = pSrc[1];
    308                   pSrc += pDownmixer->input_channel_count;
    309                   pDst += 2;
    310                   numFrames--;
    311               }
    312           }
    313           break;
    314 
    315       case DOWNMIX_TYPE_FOLD:
    316 #ifdef DOWNMIX_ALWAYS_USE_GENERIC_DOWNMIXER
    317           // bypass the optimized downmix routines for the common formats
    318           if (!Downmix_foldGeneric(
    319                   downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
    320               ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
    321               return -EINVAL;
    322           }
    323           break;
    324 #endif
    325         // optimize for the common formats
    326         switch((downmix_input_channel_mask_t)downmixInputChannelMask) {
    327         case CHANNEL_MASK_QUAD_BACK:
    328         case CHANNEL_MASK_QUAD_SIDE:
    329             Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
    330             break;
    331         case CHANNEL_MASK_5POINT1_BACK:
    332         case CHANNEL_MASK_5POINT1_SIDE:
    333             Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
    334             break;
    335         case CHANNEL_MASK_7POINT1:
    336             Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
    337             break;
    338         default:
    339             if (!Downmix_foldGeneric(
    340                     downmixInputChannelMask, pSrc, pDst, numFrames, accumulate)) {
    341                 ALOGE("Multichannel configuration 0x%" PRIx32 " is not supported", downmixInputChannelMask);
    342                 return -EINVAL;
    343             }
    344             break;
    345         }
    346         break;
    347 
    348       default:
    349         return -EINVAL;
    350     }
    351 
    352     return 0;
    353 }
    354 
    355 
    356 static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
    357         void *pCmdData, uint32_t *replySize, void *pReplyData) {
    358 
    359     downmix_module_t *pDwmModule = (downmix_module_t *) self;
    360     downmix_object_t *pDownmixer;
    361     int retsize;
    362 
    363     if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
    364         return -EINVAL;
    365     }
    366 
    367     pDownmixer = (downmix_object_t*) &pDwmModule->context;
    368 
    369     ALOGV("Downmix_Command command %" PRIu32 " cmdSize %" PRIu32, cmdCode, cmdSize);
    370 
    371     switch (cmdCode) {
    372     case EFFECT_CMD_INIT:
    373         if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
    374             return -EINVAL;
    375         }
    376         *(int *) pReplyData = Downmix_Init(pDwmModule);
    377         break;
    378 
    379     case EFFECT_CMD_SET_CONFIG:
    380         if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
    381                 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
    382             return -EINVAL;
    383         }
    384         *(int *) pReplyData = Downmix_Configure(pDwmModule,
    385                 (effect_config_t *)pCmdData, false);
    386         break;
    387 
    388     case EFFECT_CMD_RESET:
    389         Downmix_Reset(pDownmixer, false);
    390         break;
    391 
    392     case EFFECT_CMD_GET_PARAM:
    393         ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %" PRIu32 ", pReplyData: %p",
    394                 pCmdData, *replySize, pReplyData);
    395         if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
    396                 pReplyData == NULL || replySize == NULL ||
    397                 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
    398             return -EINVAL;
    399         }
    400         effect_param_t *rep = (effect_param_t *) pReplyData;
    401         memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
    402         ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %" PRId32 ", replySize %" PRIu32,
    403                 *(int32_t *)rep->data, rep->vsize);
    404         rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
    405                 rep->data + sizeof(int32_t));
    406         *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
    407         break;
    408 
    409     case EFFECT_CMD_SET_PARAM:
    410         ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %" PRIu32
    411                 ", pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
    412         if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
    413                 || pReplyData == NULL || replySize == NULL || *replySize != (int)sizeof(int32_t)) {
    414             return -EINVAL;
    415         }
    416         effect_param_t *cmd = (effect_param_t *) pCmdData;
    417         *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
    418                 cmd->vsize, cmd->data + sizeof(int32_t));
    419         break;
    420 
    421     case EFFECT_CMD_SET_PARAM_DEFERRED:
    422         //FIXME implement
    423         ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
    424         break;
    425 
    426     case EFFECT_CMD_SET_PARAM_COMMIT:
    427         //FIXME implement
    428         ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
    429         break;
    430 
    431     case EFFECT_CMD_ENABLE:
    432         if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
    433             return -EINVAL;
    434         }
    435         if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
    436             return -ENOSYS;
    437         }
    438         pDownmixer->state = DOWNMIX_STATE_ACTIVE;
    439         ALOGV("EFFECT_CMD_ENABLE() OK");
    440         *(int *)pReplyData = 0;
    441         break;
    442 
    443     case EFFECT_CMD_DISABLE:
    444         if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
    445             return -EINVAL;
    446         }
    447         if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
    448             return -ENOSYS;
    449         }
    450         pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
    451         ALOGV("EFFECT_CMD_DISABLE() OK");
    452         *(int *)pReplyData = 0;
    453         break;
    454 
    455     case EFFECT_CMD_SET_DEVICE:
    456         if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
    457             return -EINVAL;
    458         }
    459         // FIXME change type if playing on headset vs speaker
    460         ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData);
    461         break;
    462 
    463     case EFFECT_CMD_SET_VOLUME: {
    464         // audio output is always stereo => 2 channel volumes
    465         if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
    466             return -EINVAL;
    467         }
    468         // FIXME change volume
    469         ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
    470         float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
    471         float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
    472         ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
    473         break;
    474     }
    475 
    476     case EFFECT_CMD_SET_AUDIO_MODE:
    477         if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
    478             return -EINVAL;
    479         }
    480         ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData);
    481         break;
    482 
    483     case EFFECT_CMD_SET_CONFIG_REVERSE:
    484     case EFFECT_CMD_SET_INPUT_DEVICE:
    485         // these commands are ignored by a downmix effect
    486         break;
    487 
    488     default:
    489         ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode);
    490         return -EINVAL;
    491     }
    492 
    493     return 0;
    494 }
    495 
    496 
    497 int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
    498 {
    499     downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
    500 
    501     if (pDwnmxModule == NULL ||
    502             pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
    503         return -EINVAL;
    504     }
    505 
    506     memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
    507 
    508     return 0;
    509 }
    510 
    511 
    512 /*----------------------------------------------------------------------------
    513  * Downmix internal functions
    514  *--------------------------------------------------------------------------*/
    515 
    516 /*----------------------------------------------------------------------------
    517  * Downmix_Init()
    518  *----------------------------------------------------------------------------
    519  * Purpose:
    520  * Initialize downmix context and apply default parameters
    521  *
    522  * Inputs:
    523  *  pDwmModule    pointer to downmix effect module
    524  *
    525  * Outputs:
    526  *
    527  * Returns:
    528  *  0             indicates success
    529  *
    530  * Side Effects:
    531  *  updates:
    532  *           pDwmModule->context.type
    533  *           pDwmModule->context.apply_volume_correction
    534  *           pDwmModule->config.inputCfg
    535  *           pDwmModule->config.outputCfg
    536  *           pDwmModule->config.inputCfg.samplingRate
    537  *           pDwmModule->config.outputCfg.samplingRate
    538  *           pDwmModule->context.state
    539  *  doesn't set:
    540  *           pDwmModule->itfe
    541  *
    542  *----------------------------------------------------------------------------
    543  */
    544 
    545 int Downmix_Init(downmix_module_t *pDwmModule) {
    546 
    547     ALOGV("Downmix_Init module %p", pDwmModule);
    548     int ret = 0;
    549 
    550     memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
    551 
    552     pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
    553     pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    554     pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
    555     pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
    556     pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
    557     pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
    558     pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
    559 
    560     pDwmModule->config.inputCfg.samplingRate = 44100;
    561     pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
    562 
    563     // set a default value for the access mode, but should be overwritten by caller
    564     pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
    565     pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    566     pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
    567     pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
    568     pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
    569     pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
    570     pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
    571 
    572     ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
    573     if (ret != 0) {
    574         ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
    575     } else {
    576         pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
    577     }
    578 
    579     return ret;
    580 }
    581 
    582 
    583 /*----------------------------------------------------------------------------
    584  * Downmix_Configure()
    585  *----------------------------------------------------------------------------
    586  * Purpose:
    587  *  Set input and output audio configuration.
    588  *
    589  * Inputs:
    590  *  pDwmModule  pointer to downmix effect module
    591  *  pConfig     pointer to effect_config_t structure containing input
    592  *                  and output audio parameters configuration
    593  *  init        true if called from init function
    594  *
    595  * Outputs:
    596  *
    597  * Returns:
    598  *  0           indicates success
    599  *
    600  * Side Effects:
    601  *
    602  *----------------------------------------------------------------------------
    603  */
    604 
    605 int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
    606 
    607     downmix_object_t *pDownmixer = &pDwmModule->context;
    608 
    609     // Check configuration compatibility with build options, and effect capabilities
    610     if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
    611         || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
    612         || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
    613         || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
    614         ALOGE("Downmix_Configure error: invalid config");
    615         return -EINVAL;
    616     }
    617 
    618     if (&pDwmModule->config != pConfig) {
    619         memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
    620     }
    621 
    622     if (init) {
    623         pDownmixer->type = DOWNMIX_TYPE_FOLD;
    624         pDownmixer->apply_volume_correction = false;
    625         pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
    626     } else {
    627         // when configuring the effect, do not allow a blank channel mask
    628         if (pConfig->inputCfg.channels == 0) {
    629             ALOGE("Downmix_Configure error: input channel mask can't be 0");
    630             return -EINVAL;
    631         }
    632         pDownmixer->input_channel_count =
    633                 audio_channel_count_from_out_mask(pConfig->inputCfg.channels);
    634     }
    635 
    636     Downmix_Reset(pDownmixer, init);
    637 
    638     return 0;
    639 }
    640 
    641 
    642 /*----------------------------------------------------------------------------
    643  * Downmix_Reset()
    644  *----------------------------------------------------------------------------
    645  * Purpose:
    646  *  Reset internal states.
    647  *
    648  * Inputs:
    649  *  pDownmixer   pointer to downmix context
    650  *  init         true if called from init function
    651  *
    652  * Outputs:
    653 *
    654  * Returns:
    655  *  0            indicates success
    656  *
    657  * Side Effects:
    658  *
    659  *----------------------------------------------------------------------------
    660  */
    661 
    662 int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) {
    663     // nothing to do here
    664     return 0;
    665 }
    666 
    667 
    668 /*----------------------------------------------------------------------------
    669  * Downmix_setParameter()
    670  *----------------------------------------------------------------------------
    671  * Purpose:
    672  * Set a Downmix parameter
    673  *
    674  * Inputs:
    675  *  pDownmixer    handle to instance data
    676  *  param         parameter
    677  *  pValue        pointer to parameter value
    678  *  size          value size
    679  *
    680  * Outputs:
    681  *
    682  * Returns:
    683  *  0             indicates success
    684  *
    685  * Side Effects:
    686  *
    687  *----------------------------------------------------------------------------
    688  */
    689 int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) {
    690 
    691     int16_t value16;
    692     ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32,
    693             pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
    694 
    695     switch (param) {
    696 
    697       case DOWNMIX_PARAM_TYPE:
    698         if (size != sizeof(downmix_type_t)) {
    699             ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu",
    700                     size, sizeof(downmix_type_t));
    701             return -EINVAL;
    702         }
    703         value16 = *(int16_t *)pValue;
    704         ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16);
    705         if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) {
    706             ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16);
    707             return -EINVAL;
    708         } else {
    709             pDownmixer->type = (downmix_type_t) value16;
    710         break;
    711 
    712       default:
    713         ALOGE("Downmix_setParameter unknown parameter %" PRId32, param);
    714         return -EINVAL;
    715     }
    716 }
    717 
    718     return 0;
    719 } /* end Downmix_setParameter */
    720 
    721 
    722 /*----------------------------------------------------------------------------
    723  * Downmix_getParameter()
    724  *----------------------------------------------------------------------------
    725  * Purpose:
    726  * Get a Downmix parameter
    727  *
    728  * Inputs:
    729  *  pDownmixer    handle to instance data
    730  *  param         parameter
    731  *  pValue        pointer to variable to hold retrieved value
    732  *  pSize         pointer to value size: maximum size as input
    733  *
    734  * Outputs:
    735  *  *pValue updated with parameter value
    736  *  *pSize updated with actual value size
    737  *
    738  * Returns:
    739  *  0             indicates success
    740  *
    741  * Side Effects:
    742  *
    743  *----------------------------------------------------------------------------
    744  */
    745 int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) {
    746     int16_t *pValue16;
    747 
    748     switch (param) {
    749 
    750     case DOWNMIX_PARAM_TYPE:
    751       if (*pSize < sizeof(int16_t)) {
    752           ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize);
    753           return -EINVAL;
    754       }
    755       pValue16 = (int16_t *)pValue;
    756       *pValue16 = (int16_t) pDownmixer->type;
    757       *pSize = sizeof(int16_t);
    758       ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16);
    759       break;
    760 
    761     default:
    762       ALOGE("Downmix_getParameter unknown parameter %" PRId16, param);
    763       return -EINVAL;
    764     }
    765 
    766     return 0;
    767 } /* end Downmix_getParameter */
    768 
    769 
    770 /*----------------------------------------------------------------------------
    771  * Downmix_foldFromQuad()
    772  *----------------------------------------------------------------------------
    773  * Purpose:
    774  * downmix a quad signal to stereo
    775  *
    776  * Inputs:
    777  *  pSrc       quad audio samples to downmix
    778  *  numFrames  the number of quad frames to downmix
    779  *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
    780  *               or overwrite pDst (when false)
    781  *
    782  * Outputs:
    783  *  pDst       downmixed stereo audio samples
    784  *
    785  *----------------------------------------------------------------------------
    786  */
    787 void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
    788     // sample at index 0 is FL
    789     // sample at index 1 is FR
    790     // sample at index 2 is RL
    791     // sample at index 3 is RR
    792     if (accumulate) {
    793         while (numFrames) {
    794             // FL + RL
    795             pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1));
    796             // FR + RR
    797             pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1));
    798             pSrc += 4;
    799             pDst += 2;
    800             numFrames--;
    801         }
    802     } else { // same code as above but without adding and clamping pDst[i] to itself
    803         while (numFrames) {
    804             // FL + RL
    805             pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1);
    806             // FR + RR
    807             pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1);
    808             pSrc += 4;
    809             pDst += 2;
    810             numFrames--;
    811         }
    812     }
    813 }
    814 
    815 
    816 /*----------------------------------------------------------------------------
    817  * Downmix_foldFrom5Point1()
    818  *----------------------------------------------------------------------------
    819  * Purpose:
    820  * downmix a 5.1 signal to stereo
    821  *
    822  * Inputs:
    823  *  pSrc       5.1 audio samples to downmix
    824  *  numFrames  the number of 5.1 frames to downmix
    825  *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
    826  *               or overwrite pDst (when false)
    827  *
    828  * Outputs:
    829  *  pDst       downmixed stereo audio samples
    830  *
    831  *----------------------------------------------------------------------------
    832  */
    833 void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
    834     int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
    835     // sample at index 0 is FL
    836     // sample at index 1 is FR
    837     // sample at index 2 is FC
    838     // sample at index 3 is LFE
    839     // sample at index 4 is RL
    840     // sample at index 5 is RR
    841     // code is mostly duplicated between the two values of accumulate to avoid repeating the test
    842     // for every sample
    843     if (accumulate) {
    844         while (numFrames) {
    845             // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
    846             centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
    847                     + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
    848             // FL + centerPlusLfeContrib + RL
    849             lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
    850             // FR + centerPlusLfeContrib + RR
    851             rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
    852             // accumulate in destination
    853             pDst[0] = clamp16(pDst[0] + (lt >> 13));
    854             pDst[1] = clamp16(pDst[1] + (rt >> 13));
    855             pSrc += 6;
    856             pDst += 2;
    857             numFrames--;
    858         }
    859     } else { // same code as above but without adding and clamping pDst[i] to itself
    860         while (numFrames) {
    861             // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
    862             centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
    863                     + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
    864             // FL + centerPlusLfeContrib + RL
    865             lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
    866             // FR + centerPlusLfeContrib + RR
    867             rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
    868             // store in destination
    869             pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
    870             pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
    871             pSrc += 6;
    872             pDst += 2;
    873             numFrames--;
    874         }
    875     }
    876 }
    877 
    878 
    879 /*----------------------------------------------------------------------------
    880  * Downmix_foldFrom7Point1()
    881  *----------------------------------------------------------------------------
    882  * Purpose:
    883  * downmix a 7.1 signal to stereo
    884  *
    885  * Inputs:
    886  *  pSrc       7.1 audio samples to downmix
    887  *  numFrames  the number of 7.1 frames to downmix
    888  *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
    889  *               or overwrite pDst (when false)
    890  *
    891  * Outputs:
    892  *  pDst       downmixed stereo audio samples
    893  *
    894  *----------------------------------------------------------------------------
    895  */
    896 void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
    897     int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
    898     // sample at index 0 is FL
    899     // sample at index 1 is FR
    900     // sample at index 2 is FC
    901     // sample at index 3 is LFE
    902     // sample at index 4 is RL
    903     // sample at index 5 is RR
    904     // sample at index 6 is SL
    905     // sample at index 7 is SR
    906     // code is mostly duplicated between the two values of accumulate to avoid repeating the test
    907     // for every sample
    908     if (accumulate) {
    909         while (numFrames) {
    910             // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
    911             centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
    912                     + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
    913             // FL + centerPlusLfeContrib + SL + RL
    914             lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
    915             // FR + centerPlusLfeContrib + SR + RR
    916             rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
    917             //accumulate in destination
    918             pDst[0] = clamp16(pDst[0] + (lt >> 13));
    919             pDst[1] = clamp16(pDst[1] + (rt >> 13));
    920             pSrc += 8;
    921             pDst += 2;
    922             numFrames--;
    923     }
    924     } else { // same code as above but without adding and clamping pDst[i] to itself
    925         while (numFrames) {
    926             // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
    927             centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
    928                     + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
    929             // FL + centerPlusLfeContrib + SL + RL
    930             lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
    931             // FR + centerPlusLfeContrib + SR + RR
    932             rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
    933             // store in destination
    934             pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
    935             pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
    936             pSrc += 8;
    937             pDst += 2;
    938             numFrames--;
    939         }
    940     }
    941 }
    942 
    943 
    944 /*----------------------------------------------------------------------------
    945  * Downmix_foldGeneric()
    946  *----------------------------------------------------------------------------
    947  * Purpose:
    948  * downmix to stereo a multichannel signal whose format is:
    949  *  - has FL/FR
    950  *  - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right
    951  *  - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right
    952  *  - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels
    953  *  - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels
    954  * Only handles channel masks not enumerated in downmix_input_channel_mask_t
    955  *
    956  * Inputs:
    957  *  mask       the channel mask of pSrc
    958  *  pSrc       multichannel audio buffer to downmix
    959  *  numFrames  the number of multichannel frames to downmix
    960  *  accumulate whether to mix (when true) the result of the downmix with the contents of pDst,
    961  *               or overwrite pDst (when false)
    962  *
    963  * Outputs:
    964  *  pDst       downmixed stereo audio samples
    965  *
    966  * Returns: false if multichannel format is not supported
    967  *
    968  *----------------------------------------------------------------------------
    969  */
    970 bool Downmix_foldGeneric(
    971         uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
    972     // check against unsupported channels
    973     if (mask & kUnsupported) {
    974         ALOGE("Unsupported channels (top or front left/right of center)");
    975         return false;
    976     }
    977     // verify has FL/FR
    978     if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) {
    979         ALOGE("Front channels must be present");
    980         return false;
    981     }
    982     // verify uses SIDE as a pair (ok if not using SIDE at all)
    983     bool hasSides = false;
    984     if ((mask & kSides) != 0) {
    985         if ((mask & kSides) != kSides) {
    986             ALOGE("Side channels must be used as a pair");
    987             return false;
    988         }
    989         hasSides = true;
    990     }
    991     // verify uses BACK as a pair (ok if not using BACK at all)
    992     bool hasBacks = false;
    993     if ((mask & kBacks) != 0) {
    994         if ((mask & kBacks) != kBacks) {
    995             ALOGE("Back channels must be used as a pair");
    996             return false;
    997         }
    998         hasBacks = true;
    999     }
   1000 
   1001     const int numChan = audio_channel_count_from_out_mask(mask);
   1002     const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER);
   1003     const bool hasLFE =
   1004             ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY);
   1005     const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER);
   1006     // compute at what index each channel is: samples will be in the following order:
   1007     //   FL FR FC LFE BL BR BC SL SR
   1008     // when a channel is not present, its index is set to the same as the index of the preceding
   1009     // channel
   1010     const int indexFC  = hasFC    ? 2            : 1;        // front center
   1011     const int indexLFE = hasLFE   ? indexFC + 1  : indexFC;  // low frequency
   1012     const int indexBL  = hasBacks ? indexLFE + 1 : indexLFE; // back left
   1013     const int indexBR  = hasBacks ? indexBL + 1  : indexBL;  // back right
   1014     const int indexBC  = hasBC    ? indexBR + 1  : indexBR;  // back center
   1015     const int indexSL  = hasSides ? indexBC + 1  : indexBC;  // side left
   1016     const int indexSR  = hasSides ? indexSL + 1  : indexSL;  // side right
   1017 
   1018     int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format
   1019     // code is mostly duplicated between the two values of accumulate to avoid repeating the test
   1020     // for every sample
   1021     if (accumulate) {
   1022         while (numFrames) {
   1023             // compute contribution of FC, BC and LFE
   1024             centersLfeContrib = 0;
   1025             if (hasFC)  { centersLfeContrib += pSrc[indexFC]; }
   1026             if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
   1027             if (hasBC)  { centersLfeContrib += pSrc[indexBC]; }
   1028             centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
   1029             // always has FL/FR
   1030             lt = (pSrc[0] << 12);
   1031             rt = (pSrc[1] << 12);
   1032             // mix in sides and backs
   1033             if (hasSides) {
   1034                 lt += pSrc[indexSL] << 12;
   1035                 rt += pSrc[indexSR] << 12;
   1036             }
   1037             if (hasBacks) {
   1038                 lt += pSrc[indexBL] << 12;
   1039                 rt += pSrc[indexBR] << 12;
   1040             }
   1041             lt += centersLfeContrib;
   1042             rt += centersLfeContrib;
   1043             // accumulate in destination
   1044             pDst[0] = clamp16(pDst[0] + (lt >> 13));
   1045             pDst[1] = clamp16(pDst[1] + (rt >> 13));
   1046             pSrc += numChan;
   1047             pDst += 2;
   1048             numFrames--;
   1049         }
   1050     } else {
   1051         while (numFrames) {
   1052             // compute contribution of FC, BC and LFE
   1053             centersLfeContrib = 0;
   1054             if (hasFC)  { centersLfeContrib += pSrc[indexFC]; }
   1055             if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; }
   1056             if (hasBC)  { centersLfeContrib += pSrc[indexBC]; }
   1057             centersLfeContrib *= MINUS_3_DB_IN_Q19_12;
   1058             // always has FL/FR
   1059             lt = (pSrc[0] << 12);
   1060             rt = (pSrc[1] << 12);
   1061             // mix in sides and backs
   1062             if (hasSides) {
   1063                 lt += pSrc[indexSL] << 12;
   1064                 rt += pSrc[indexSR] << 12;
   1065             }
   1066             if (hasBacks) {
   1067                 lt += pSrc[indexBL] << 12;
   1068                 rt += pSrc[indexBR] << 12;
   1069             }
   1070             lt += centersLfeContrib;
   1071             rt += centersLfeContrib;
   1072             // store in destination
   1073             pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above
   1074             pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above
   1075             pSrc += numChan;
   1076             pDst += 2;
   1077             numFrames--;
   1078         }
   1079     }
   1080     return true;
   1081 }
   1082