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