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