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