EffectDownmix.c revision f28c8792f64e10c3c477d86bf4804a8566ff524e
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "EffectDownmix" 18//#define LOG_NDEBUG 0 19#include <cutils/log.h> 20#include <stdlib.h> 21#include <string.h> 22#include <stdbool.h> 23#include "EffectDownmix.h" 24 25#define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896 26 27// effect_handle_t interface implementation for downmix effect 28const struct effect_interface_s gDownmixInterface = { 29 Downmix_Process, 30 Downmix_Command, 31 Downmix_GetDescriptor, 32 NULL /* no process_reverse function, no reference stream needed */ 33}; 34 35audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { 36 tag : AUDIO_EFFECT_LIBRARY_TAG, 37 version : EFFECT_LIBRARY_API_VERSION, 38 name : "Downmix Library", 39 implementor : "The Android Open Source Project", 40 query_num_effects : DownmixLib_QueryNumberEffects, 41 query_effect : DownmixLib_QueryEffect, 42 create_effect : DownmixLib_Create, 43 release_effect : DownmixLib_Release, 44 get_descriptor : DownmixLib_GetDescriptor, 45}; 46 47 48// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f 49static const effect_descriptor_t gDownmixDescriptor = { 50 EFFECT_UIID_DOWNMIX__, //type 51 {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid 52 EFFECT_CONTROL_API_VERSION, 53 EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST, 54 0, //FIXME what value should be reported? // cpu load 55 0, //FIXME what value should be reported? // memory usage 56 "Multichannel Downmix To Stereo", // human readable effect name 57 "The Android Open Source Project" // human readable effect implementor name 58}; 59 60// gDescriptors contains pointers to all defined effect descriptor in this library 61static const effect_descriptor_t * const gDescriptors[] = { 62 &gDownmixDescriptor 63}; 64 65// number of effects in this library 66const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *); 67 68 69/*---------------------------------------------------------------------------- 70 * Effect API implementation 71 *--------------------------------------------------------------------------*/ 72 73/*--- Effect Library Interface Implementation ---*/ 74 75int32_t DownmixLib_QueryNumberEffects(uint32_t *pNumEffects) { 76 ALOGV("DownmixLib_QueryNumberEffects()"); 77 *pNumEffects = kNbEffects; 78 return 0; 79} 80 81int32_t DownmixLib_QueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) { 82 ALOGV("DownmixLib_QueryEffect() index=%d", index); 83 if (pDescriptor == NULL) { 84 return -EINVAL; 85 } 86 if (index >= (uint32_t)kNbEffects) { 87 return -EINVAL; 88 } 89 memcpy(pDescriptor, gDescriptors[index], sizeof(effect_descriptor_t)); 90 return 0; 91} 92 93 94int32_t DownmixLib_Create(const effect_uuid_t *uuid, 95 int32_t sessionId, 96 int32_t ioId, 97 effect_handle_t *pHandle) { 98 int ret; 99 int i; 100 downmix_module_t *module; 101 const effect_descriptor_t *desc; 102 103 ALOGV("DownmixLib_Create()"); 104 105 if (pHandle == NULL || uuid == NULL) { 106 return -EINVAL; 107 } 108 109 for (i = 0 ; i < kNbEffects ; i++) { 110 desc = gDescriptors[i]; 111 if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) { 112 break; 113 } 114 } 115 116 if (i == kNbEffects) { 117 return -ENOENT; 118 } 119 120 module = malloc(sizeof(downmix_module_t)); 121 122 module->itfe = &gDownmixInterface; 123 124 module->context.state = DOWNMIX_STATE_UNINITIALIZED; 125 126 ret = Downmix_Init(module); 127 if (ret < 0) { 128 ALOGW("DownmixLib_Create() init failed"); 129 free(module); 130 return ret; 131 } 132 133 *pHandle = (effect_handle_t) module; 134 135 ALOGV("DownmixLib_Create() %p , size %d", module, sizeof(downmix_module_t)); 136 137 return 0; 138} 139 140 141int32_t DownmixLib_Release(effect_handle_t handle) { 142 downmix_module_t *pDwmModule = (downmix_module_t *)handle; 143 144 ALOGV("DownmixLib_Release() %p", handle); 145 if (handle == NULL) { 146 return -EINVAL; 147 } 148 149 pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED; 150 151 free(pDwmModule); 152 return 0; 153} 154 155 156int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) { 157 ALOGV("DownmixLib_GetDescriptor()"); 158 int i; 159 160 if (pDescriptor == NULL || uuid == NULL){ 161 ALOGE("DownmixLib_Create() called with NULL pointer"); 162 return -EINVAL; 163 } 164 ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects); 165 for (i = 0; i < kNbEffects; i++) { 166 ALOGV("DownmixLib_GetDescriptor() i=%d", i); 167 if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) { 168 memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t)); 169 ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %x", 170 i, gDescriptors[i]->uuid.timeLow); 171 return 0; 172 } 173 } 174 175 return -EINVAL; 176} 177 178 179/*--- Effect Control Interface Implementation ---*/ 180 181static int Downmix_Process(effect_handle_t self, 182 audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) { 183 184 downmix_object_t *pDownmixer; 185 int16_t *pSrc, *pDst; 186 downmix_module_t *pDwmModule = (downmix_module_t *)self; 187 188 if (pDwmModule == NULL) { 189 return -EINVAL; 190 } 191 192 if (inBuffer == NULL || inBuffer->raw == NULL || 193 outBuffer == NULL || outBuffer->raw == NULL || 194 inBuffer->frameCount != outBuffer->frameCount) { 195 return -EINVAL; 196 } 197 198 pDownmixer = (downmix_object_t*) &pDwmModule->context; 199 200 if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) { 201 ALOGE("Downmix_Process error: trying to use an uninitialized downmixer"); 202 return -EINVAL; 203 } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) { 204 ALOGE("Downmix_Process error: trying to use a non-configured downmixer"); 205 return -ENODATA; 206 } 207 208 pSrc = inBuffer->s16; 209 pDst = outBuffer->s16; 210 size_t numFrames = outBuffer->frameCount; 211 212 const bool accumulate = 213 (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE); 214 215 switch(pDownmixer->type) { 216 217 case DOWNMIX_TYPE_STRIP: 218 if (accumulate) { 219 while (numFrames) { 220 pDst[0] = clamp16(pDst[0] + pSrc[0]); 221 pDst[1] = clamp16(pDst[1] + pSrc[1]); 222 pSrc += pDownmixer->input_channel_count; 223 pDst += 2; 224 numFrames--; 225 } 226 } else { 227 while (numFrames) { 228 pDst[0] = pSrc[0]; 229 pDst[1] = pSrc[1]; 230 pSrc += pDownmixer->input_channel_count; 231 pDst += 2; 232 numFrames--; 233 } 234 } 235 break; 236 237 case DOWNMIX_TYPE_FOLD: 238 // optimize for the common formats 239 switch(pDwmModule->config.inputCfg.channels) { 240 case AUDIO_CHANNEL_OUT_QUAD: 241 Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate); 242 break; 243 case AUDIO_CHANNEL_OUT_SURROUND: 244 Downmix_foldFromSurround(pSrc, pDst, numFrames, accumulate); 245 break; 246 case AUDIO_CHANNEL_OUT_5POINT1: 247 Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate); 248 break; 249 case AUDIO_CHANNEL_OUT_7POINT1: 250 Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate); 251 break; 252 default: 253 // FIXME implement generic downmix 254 ALOGE("Multichannel configurations other than quad, 4.0, 5.1 and 7.1 are not supported"); 255 break; 256 } 257 break; 258 259 default: 260 return -EINVAL; 261 } 262 263 return 0; 264} 265 266 267static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, 268 void *pCmdData, uint32_t *replySize, void *pReplyData) { 269 270 downmix_module_t *pDwmModule = (downmix_module_t *) self; 271 downmix_object_t *pDownmixer; 272 int retsize; 273 274 if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) { 275 return -EINVAL; 276 } 277 278 pDownmixer = (downmix_object_t*) &pDwmModule->context; 279 280 ALOGV("Downmix_Command command %d cmdSize %d",cmdCode, cmdSize); 281 282 switch (cmdCode) { 283 case EFFECT_CMD_INIT: 284 if (pReplyData == NULL || *replySize != sizeof(int)) { 285 return -EINVAL; 286 } 287 *(int *) pReplyData = Downmix_Init(pDwmModule); 288 break; 289 290 case EFFECT_CMD_SET_CONFIG: 291 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) 292 || pReplyData == NULL || *replySize != sizeof(int)) { 293 return -EINVAL; 294 } 295 *(int *) pReplyData = Downmix_Configure(pDwmModule, 296 (effect_config_t *)pCmdData, false); 297 break; 298 299 case EFFECT_CMD_RESET: 300 Downmix_Reset(pDownmixer, false); 301 break; 302 303 case EFFECT_CMD_GET_PARAM: 304 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %d, pReplyData: %p", 305 pCmdData, *replySize, pReplyData); 306 if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) || 307 pReplyData == NULL || 308 *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) { 309 return -EINVAL; 310 } 311 effect_param_t *rep = (effect_param_t *) pReplyData; 312 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t)); 313 ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %d", 314 *(int32_t *)rep->data, rep->vsize); 315 rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize, 316 rep->data + sizeof(int32_t)); 317 *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize; 318 break; 319 320 case EFFECT_CMD_SET_PARAM: 321 ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %d, " \ 322 "pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData); 323 if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t))) 324 || pReplyData == NULL || *replySize != (int)sizeof(int32_t)) { 325 return -EINVAL; 326 } 327 effect_param_t *cmd = (effect_param_t *) pCmdData; 328 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data, 329 cmd->vsize, cmd->data + sizeof(int32_t)); 330 break; 331 332 case EFFECT_CMD_SET_PARAM_DEFERRED: 333 //FIXME implement 334 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME"); 335 break; 336 337 case EFFECT_CMD_SET_PARAM_COMMIT: 338 //FIXME implement 339 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME"); 340 break; 341 342 case EFFECT_CMD_ENABLE: 343 if (pReplyData == NULL || *replySize != sizeof(int)) { 344 return -EINVAL; 345 } 346 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) { 347 return -ENOSYS; 348 } 349 pDownmixer->state = DOWNMIX_STATE_ACTIVE; 350 ALOGV("EFFECT_CMD_ENABLE() OK"); 351 *(int *)pReplyData = 0; 352 break; 353 354 case EFFECT_CMD_DISABLE: 355 if (pReplyData == NULL || *replySize != sizeof(int)) { 356 return -EINVAL; 357 } 358 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) { 359 return -ENOSYS; 360 } 361 pDownmixer->state = DOWNMIX_STATE_INITIALIZED; 362 ALOGV("EFFECT_CMD_DISABLE() OK"); 363 *(int *)pReplyData = 0; 364 break; 365 366 case EFFECT_CMD_SET_DEVICE: 367 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) { 368 return -EINVAL; 369 } 370 // FIXME change type if playing on headset vs speaker 371 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08x", *(uint32_t *)pCmdData); 372 break; 373 374 case EFFECT_CMD_SET_VOLUME: { 375 // audio output is always stereo => 2 channel volumes 376 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) { 377 return -EINVAL; 378 } 379 // FIXME change volume 380 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME"); 381 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24); 382 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24); 383 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right); 384 break; 385 } 386 387 case EFFECT_CMD_SET_AUDIO_MODE: 388 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) { 389 return -EINVAL; 390 } 391 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %d", *(uint32_t *)pCmdData); 392 break; 393 394 case EFFECT_CMD_SET_CONFIG_REVERSE: 395 case EFFECT_CMD_SET_INPUT_DEVICE: 396 // these commands are ignored by a downmix effect 397 break; 398 399 default: 400 ALOGW("Downmix_Command invalid command %d",cmdCode); 401 return -EINVAL; 402 } 403 404 return 0; 405} 406 407 408int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor) 409{ 410 downmix_module_t *pDwnmxModule = (downmix_module_t *) self; 411 412 if (pDwnmxModule == NULL || 413 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) { 414 return -EINVAL; 415 } 416 417 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t)); 418 419 return 0; 420} 421 422 423/*---------------------------------------------------------------------------- 424 * Downmix internal functions 425 *--------------------------------------------------------------------------*/ 426 427/*---------------------------------------------------------------------------- 428 * Downmix_Init() 429 *---------------------------------------------------------------------------- 430 * Purpose: 431 * Initialize downmix context and apply default parameters 432 * 433 * Inputs: 434 * pDwmModule pointer to downmix effect module 435 * 436 * Outputs: 437 * 438 * Returns: 439 * 0 indicates success 440 * 441 * Side Effects: 442 * updates: 443 * pDwmModule->context.type 444 * pDwmModule->context.apply_volume_correction 445 * pDwmModule->config.inputCfg 446 * pDwmModule->config.outputCfg 447 * pDwmModule->config.inputCfg.samplingRate 448 * pDwmModule->config.outputCfg.samplingRate 449 * pDwmModule->context.state 450 * doesn't set: 451 * pDwmModule->itfe 452 * 453 *---------------------------------------------------------------------------- 454 */ 455 456int Downmix_Init(downmix_module_t *pDwmModule) { 457 458 ALOGV("Downmix_Init module %p", pDwmModule); 459 int ret = 0; 460 461 memset(&pDwmModule->context, 0, sizeof(downmix_object_t)); 462 463 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; 464 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; 465 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1; 466 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL; 467 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL; 468 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL; 469 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL; 470 471 pDwmModule->config.inputCfg.samplingRate = 44100; 472 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate; 473 474 // set a default value for the access mode, but should be overwritten by caller 475 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; 476 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; 477 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; 478 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL; 479 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL; 480 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL; 481 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL; 482 483 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true); 484 if (ret != 0) { 485 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule); 486 } else { 487 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED; 488 } 489 490 return ret; 491} 492 493 494/*---------------------------------------------------------------------------- 495 * Downmix_Configure() 496 *---------------------------------------------------------------------------- 497 * Purpose: 498 * Set input and output audio configuration. 499 * 500 * Inputs: 501 * pDwmModule pointer to downmix effect module 502 * pConfig pointer to effect_config_t structure containing input 503 * and output audio parameters configuration 504 * init true if called from init function 505 * 506 * Outputs: 507 * 508 * Returns: 509 * 0 indicates success 510 * 511 * Side Effects: 512 * 513 *---------------------------------------------------------------------------- 514 */ 515 516int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) { 517 518 downmix_object_t *pDownmixer = &pDwmModule->context; 519 520 // Check configuration compatibility with build options, and effect capabilities 521 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate 522 || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS 523 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT 524 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) { 525 ALOGE("Downmix_Configure error: invalid config"); 526 return -EINVAL; 527 } 528 529 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t)); 530 531 if (init) { 532 pDownmixer->type = DOWNMIX_TYPE_FOLD; 533 pDownmixer->apply_volume_correction = false; 534 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1 535 } else { 536 // when configuring the effect, do not allow a blank channel mask 537 if (pConfig->inputCfg.channels == 0) { 538 ALOGE("Downmix_Configure error: input channel mask can't be 0"); 539 return -EINVAL; 540 } 541 pDownmixer->input_channel_count = popcount(pConfig->inputCfg.channels); 542 } 543 544 Downmix_Reset(pDownmixer, init); 545 546 return 0; 547} 548 549 550/*---------------------------------------------------------------------------- 551 * Downmix_Reset() 552 *---------------------------------------------------------------------------- 553 * Purpose: 554 * Reset internal states. 555 * 556 * Inputs: 557 * pDownmixer pointer to downmix context 558 * init true if called from init function 559 * 560 * Outputs: 561* 562 * Returns: 563 * 0 indicates success 564 * 565 * Side Effects: 566 * 567 *---------------------------------------------------------------------------- 568 */ 569 570int Downmix_Reset(downmix_object_t *pDownmixer, bool init) { 571 // nothing to do here 572 return 0; 573} 574 575 576/*---------------------------------------------------------------------------- 577 * Downmix_setParameter() 578 *---------------------------------------------------------------------------- 579 * Purpose: 580 * Set a Downmix parameter 581 * 582 * Inputs: 583 * pDownmixer handle to instance data 584 * param parameter 585 * pValue pointer to parameter value 586 * size value size 587 * 588 * Outputs: 589 * 590 * Returns: 591 * 0 indicates success 592 * 593 * Side Effects: 594 * 595 *---------------------------------------------------------------------------- 596 */ 597int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, size_t size, void *pValue) { 598 599 int16_t value16; 600 ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d", 601 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue); 602 603 switch (param) { 604 605 case DOWNMIX_PARAM_TYPE: 606 if (size != sizeof(downmix_type_t)) { 607 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %d, should be %d", 608 size, sizeof(downmix_type_t)); 609 return -EINVAL; 610 } 611 value16 = *(int16_t *)pValue; 612 ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16); 613 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) { 614 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16); 615 return -EINVAL; 616 } else { 617 pDownmixer->type = (downmix_type_t) value16; 618 break; 619 620 default: 621 ALOGE("Downmix_setParameter unknown parameter %d", param); 622 return -EINVAL; 623 } 624} 625 626 return 0; 627} /* end Downmix_setParameter */ 628 629 630/*---------------------------------------------------------------------------- 631 * Downmix_getParameter() 632 *---------------------------------------------------------------------------- 633 * Purpose: 634 * Get a Downmix parameter 635 * 636 * Inputs: 637 * pDownmixer handle to instance data 638 * param parameter 639 * pValue pointer to variable to hold retrieved value 640 * pSize pointer to value size: maximum size as input 641 * 642 * Outputs: 643 * *pValue updated with parameter value 644 * *pSize updated with actual value size 645 * 646 * Returns: 647 * 0 indicates success 648 * 649 * Side Effects: 650 * 651 *---------------------------------------------------------------------------- 652 */ 653int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, size_t *pSize, void *pValue) { 654 int16_t *pValue16; 655 656 switch (param) { 657 658 case DOWNMIX_PARAM_TYPE: 659 if (*pSize < sizeof(int16_t)) { 660 ALOGE("Downmix_getParameter invalid parameter size %d for DOWNMIX_PARAM_TYPE", *pSize); 661 return -EINVAL; 662 } 663 pValue16 = (int16_t *)pValue; 664 *pValue16 = (int16_t) pDownmixer->type; 665 *pSize = sizeof(int16_t); 666 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16); 667 break; 668 669 default: 670 ALOGE("Downmix_getParameter unknown parameter %d", param); 671 return -EINVAL; 672 } 673 674 return 0; 675} /* end Downmix_getParameter */ 676 677 678/*---------------------------------------------------------------------------- 679 * Downmix_foldFromQuad() 680 *---------------------------------------------------------------------------- 681 * Purpose: 682 * downmix a quad signal to stereo 683 * 684 * Inputs: 685 * pSrc quad audio samples to downmix 686 * numFrames the number of quad frames to downmix 687 * 688 * Outputs: 689 * pDst downmixed stereo audio samples 690 * 691 *---------------------------------------------------------------------------- 692 */ 693void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 694 // sample at index 0 is FL 695 // sample at index 1 is FR 696 // sample at index 2 is RL 697 // sample at index 3 is RR 698 if (accumulate) { 699 while (numFrames) { 700 // FL + RL 701 pDst[0] = clamp16(pDst[0] + pSrc[0] + pSrc[2]); 702 // FR + RR 703 pDst[1] = clamp16(pDst[1] + pSrc[1] + pSrc[3]); 704 pSrc += 4; 705 pDst += 2; 706 numFrames--; 707 } 708 } else { // same code as above but without adding and clamping pDst[i] to itself 709 while (numFrames) { 710 // FL + RL 711 pDst[0] = clamp16(pSrc[0] + pSrc[2]); 712 // FR + RR 713 pDst[1] = clamp16(pSrc[1] + pSrc[3]); 714 pSrc += 4; 715 pDst += 2; 716 numFrames--; 717 } 718 } 719} 720 721 722/*---------------------------------------------------------------------------- 723 * Downmix_foldFromSurround() 724 *---------------------------------------------------------------------------- 725 * Purpose: 726 * downmix a "surround sound" (mono rear) signal to stereo 727 * 728 * Inputs: 729 * pSrc surround signal to downmix 730 * numFrames the number of surround frames to downmix 731 * 732 * Outputs: 733 * pDst downmixed stereo audio samples 734 * 735 *---------------------------------------------------------------------------- 736 */ 737void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 738 int32_t lt, rt, centerPlusRearContrib; // samples in Q19.12 format 739 // sample at index 0 is FL 740 // sample at index 1 is FR 741 // sample at index 2 is FC 742 // sample at index 3 is RC 743 if (accumulate) { 744 while (numFrames) { 745 // centerPlusRearContrib = FC(-3dB) + RC(-3dB) 746 centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 747 // FL + centerPlusRearContrib 748 lt = (pSrc[0] << 12) + centerPlusRearContrib; 749 // FR + centerPlusRearContrib 750 rt = (pSrc[1] << 12) + centerPlusRearContrib; 751 pDst[0] = clamp16(pDst[0] + (lt >> 12)); 752 pDst[1] = clamp16(pDst[1] + (rt >> 12)); 753 pSrc += 4; 754 pDst += 2; 755 numFrames--; 756 } 757 } else { // same code as above but without adding and clamping pDst[i] to itself 758 while (numFrames) { 759 // centerPlusRearContrib = FC(-3dB) + RC(-3dB) 760 centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 761 // FL + centerPlusRearContrib 762 lt = (pSrc[0] << 12) + centerPlusRearContrib; 763 // FR + centerPlusRearContrib 764 rt = (pSrc[1] << 12) + centerPlusRearContrib; 765 pDst[0] = clamp16(lt >> 12); 766 pDst[1] = clamp16(rt >> 12); 767 pSrc += 4; 768 pDst += 2; 769 numFrames--; 770 } 771 } 772} 773 774 775/*---------------------------------------------------------------------------- 776 * Downmix_foldFrom5Point1() 777 *---------------------------------------------------------------------------- 778 * Purpose: 779 * downmix a 5.1 signal to stereo 780 * 781 * Inputs: 782 * pSrc 5.1 audio samples to downmix 783 * numFrames the number of 5.1 frames to downmix 784 * 785 * Outputs: 786 * pDst downmixed stereo audio samples 787 * 788 *---------------------------------------------------------------------------- 789 */ 790void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 791 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format 792 // sample at index 0 is FL 793 // sample at index 1 is FR 794 // sample at index 2 is FC 795 // sample at index 3 is LFE 796 // sample at index 4 is RL 797 // sample at index 5 is RR 798 if (accumulate) { 799 while (numFrames) { 800 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 801 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 802 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 803 // FL + centerPlusLfeContrib + RL 804 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12); 805 // FR + centerPlusLfeContrib + RR 806 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12); 807 pDst[0] = clamp16(pDst[0] + (lt >> 12)); 808 pDst[1] = clamp16(pDst[1] + (rt >> 12)); 809 pSrc += 6; 810 pDst += 2; 811 numFrames--; 812 } 813 } else { // same code as above but without adding and clamping pDst[i] to itself 814 while (numFrames) { 815 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 816 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 817 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 818 // FL + centerPlusLfeContrib + RL 819 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12); 820 // FR + centerPlusLfeContrib + RR 821 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12); 822 pDst[0] = clamp16(lt >> 12); 823 pDst[1] = clamp16(rt >> 12); 824 pSrc += 6; 825 pDst += 2; 826 numFrames--; 827 } 828 } 829} 830 831 832/*---------------------------------------------------------------------------- 833 * Downmix_foldFrom7Point1() 834 *---------------------------------------------------------------------------- 835 * Purpose: 836 * downmix a 7.1 signal to stereo 837 * 838 * Inputs: 839 * pSrc 7.1 audio samples to downmix 840 * numFrames the number of 7.1 frames to downmix 841 * 842 * Outputs: 843 * pDst downmixed stereo audio samples 844 * 845 *---------------------------------------------------------------------------- 846 */ 847void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 848 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format 849 // sample at index 0 is FL 850 // sample at index 1 is FR 851 // sample at index 2 is FC 852 // sample at index 3 is LFE 853 // sample at index 4 is RL 854 // sample at index 5 is RR 855 // sample at index 6 is SL 856 // sample at index 7 is SR 857 if (accumulate) { 858 while (numFrames) { 859 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 860 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 861 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 862 // FL + centerPlusLfeContrib + SL + RL 863 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12); 864 // FR + centerPlusLfeContrib + SR + RR 865 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12); 866 pDst[0] = clamp16(lt >> 12); 867 pDst[1] = clamp16(rt >> 12); 868 pSrc += 8; 869 pDst += 2; 870 numFrames--; 871 } 872 } else { // same code as above but without adding and clamping pDst[i] to itself 873 while (numFrames) { 874 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 875 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 876 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 877 // FL + centerPlusLfeContrib + SL + RL 878 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12); 879 // FR + centerPlusLfeContrib + SR + RR 880 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12); 881 pDst[0] = clamp16(pDst[0] + (lt >> 12)); 882 pDst[1] = clamp16(pDst[1] + (rt >> 12)); 883 pSrc += 8; 884 pDst += 2; 885 numFrames--; 886 } 887 } 888} 889 890