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