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