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