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