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