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