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