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