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