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