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 *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data, 449 cmd->vsize, cmd->data + sizeof(int32_t)); 450 break; 451 452 case EFFECT_CMD_SET_PARAM_DEFERRED: 453 //FIXME implement 454 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME"); 455 break; 456 457 case EFFECT_CMD_SET_PARAM_COMMIT: 458 //FIXME implement 459 ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME"); 460 break; 461 462 case EFFECT_CMD_ENABLE: 463 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) { 464 return -EINVAL; 465 } 466 if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) { 467 return -ENOSYS; 468 } 469 pDownmixer->state = DOWNMIX_STATE_ACTIVE; 470 ALOGV("EFFECT_CMD_ENABLE() OK"); 471 *(int *)pReplyData = 0; 472 break; 473 474 case EFFECT_CMD_DISABLE: 475 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) { 476 return -EINVAL; 477 } 478 if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) { 479 return -ENOSYS; 480 } 481 pDownmixer->state = DOWNMIX_STATE_INITIALIZED; 482 ALOGV("EFFECT_CMD_DISABLE() OK"); 483 *(int *)pReplyData = 0; 484 break; 485 486 case EFFECT_CMD_SET_DEVICE: 487 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) { 488 return -EINVAL; 489 } 490 // FIXME change type if playing on headset vs speaker 491 ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08" PRIx32, *(uint32_t *)pCmdData); 492 break; 493 494 case EFFECT_CMD_SET_VOLUME: { 495 // audio output is always stereo => 2 channel volumes 496 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) { 497 return -EINVAL; 498 } 499 // FIXME change volume 500 ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME"); 501 float left = (float)(*(uint32_t *)pCmdData) / (1 << 24); 502 float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24); 503 ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right); 504 break; 505 } 506 507 case EFFECT_CMD_SET_AUDIO_MODE: 508 if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) { 509 return -EINVAL; 510 } 511 ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %" PRIu32, *(uint32_t *)pCmdData); 512 break; 513 514 case EFFECT_CMD_SET_CONFIG_REVERSE: 515 case EFFECT_CMD_SET_INPUT_DEVICE: 516 // these commands are ignored by a downmix effect 517 break; 518 519 default: 520 ALOGW("Downmix_Command invalid command %" PRIu32, cmdCode); 521 return -EINVAL; 522 } 523 524 return 0; 525} 526 527 528int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor) 529{ 530 downmix_module_t *pDwnmxModule = (downmix_module_t *) self; 531 532 if (pDwnmxModule == NULL || 533 pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) { 534 return -EINVAL; 535 } 536 537 memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t)); 538 539 return 0; 540} 541 542 543/*---------------------------------------------------------------------------- 544 * Downmix internal functions 545 *--------------------------------------------------------------------------*/ 546 547/*---------------------------------------------------------------------------- 548 * Downmix_Init() 549 *---------------------------------------------------------------------------- 550 * Purpose: 551 * Initialize downmix context and apply default parameters 552 * 553 * Inputs: 554 * pDwmModule pointer to downmix effect module 555 * 556 * Outputs: 557 * 558 * Returns: 559 * 0 indicates success 560 * 561 * Side Effects: 562 * updates: 563 * pDwmModule->context.type 564 * pDwmModule->context.apply_volume_correction 565 * pDwmModule->config.inputCfg 566 * pDwmModule->config.outputCfg 567 * pDwmModule->config.inputCfg.samplingRate 568 * pDwmModule->config.outputCfg.samplingRate 569 * pDwmModule->context.state 570 * doesn't set: 571 * pDwmModule->itfe 572 * 573 *---------------------------------------------------------------------------- 574 */ 575 576int Downmix_Init(downmix_module_t *pDwmModule) { 577 578 ALOGV("Downmix_Init module %p", pDwmModule); 579 int ret = 0; 580 581 memset(&pDwmModule->context, 0, sizeof(downmix_object_t)); 582 583 pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; 584 pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; 585 pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1; 586 pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL; 587 pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL; 588 pDwmModule->config.inputCfg.bufferProvider.cookie = NULL; 589 pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL; 590 591 pDwmModule->config.inputCfg.samplingRate = 44100; 592 pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate; 593 594 // set a default value for the access mode, but should be overwritten by caller 595 pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; 596 pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; 597 pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; 598 pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL; 599 pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL; 600 pDwmModule->config.outputCfg.bufferProvider.cookie = NULL; 601 pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL; 602 603 ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true); 604 if (ret != 0) { 605 ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule); 606 } else { 607 pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED; 608 } 609 610 return ret; 611} 612 613 614/*---------------------------------------------------------------------------- 615 * Downmix_Configure() 616 *---------------------------------------------------------------------------- 617 * Purpose: 618 * Set input and output audio configuration. 619 * 620 * Inputs: 621 * pDwmModule pointer to downmix effect module 622 * pConfig pointer to effect_config_t structure containing input 623 * and output audio parameters configuration 624 * init true if called from init function 625 * 626 * Outputs: 627 * 628 * Returns: 629 * 0 indicates success 630 * 631 * Side Effects: 632 * 633 *---------------------------------------------------------------------------- 634 */ 635 636int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) { 637 638 downmix_object_t *pDownmixer = &pDwmModule->context; 639 640 // Check configuration compatibility with build options, and effect capabilities 641 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate 642 || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS 643 || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT 644 || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) { 645 ALOGE("Downmix_Configure error: invalid config"); 646 return -EINVAL; 647 } 648 649 if (&pDwmModule->config != pConfig) { 650 memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t)); 651 } 652 653 if (init) { 654 pDownmixer->type = DOWNMIX_TYPE_FOLD; 655 pDownmixer->apply_volume_correction = false; 656 pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1 657 } else { 658 // when configuring the effect, do not allow a blank or unsupported channel mask 659 if (!Downmix_validChannelMask(pConfig->inputCfg.channels)) { 660 ALOGE("Downmix_Configure error: input channel mask(0x%x) not supported", 661 pConfig->inputCfg.channels); 662 return -EINVAL; 663 } 664 pDownmixer->input_channel_count = 665 audio_channel_count_from_out_mask(pConfig->inputCfg.channels); 666 } 667 668 Downmix_Reset(pDownmixer, init); 669 670 return 0; 671} 672 673 674/*---------------------------------------------------------------------------- 675 * Downmix_Reset() 676 *---------------------------------------------------------------------------- 677 * Purpose: 678 * Reset internal states. 679 * 680 * Inputs: 681 * pDownmixer pointer to downmix context 682 * init true if called from init function 683 * 684 * Outputs: 685* 686 * Returns: 687 * 0 indicates success 688 * 689 * Side Effects: 690 * 691 *---------------------------------------------------------------------------- 692 */ 693 694int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) { 695 // nothing to do here 696 return 0; 697} 698 699 700/*---------------------------------------------------------------------------- 701 * Downmix_setParameter() 702 *---------------------------------------------------------------------------- 703 * Purpose: 704 * Set a Downmix parameter 705 * 706 * Inputs: 707 * pDownmixer handle to instance data 708 * param parameter 709 * pValue pointer to parameter value 710 * size value size 711 * 712 * Outputs: 713 * 714 * Returns: 715 * 0 indicates success 716 * 717 * Side Effects: 718 * 719 *---------------------------------------------------------------------------- 720 */ 721int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) { 722 723 int16_t value16; 724 ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32, 725 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue); 726 727 switch (param) { 728 729 case DOWNMIX_PARAM_TYPE: 730 if (size != sizeof(downmix_type_t)) { 731 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu", 732 size, sizeof(downmix_type_t)); 733 return -EINVAL; 734 } 735 value16 = *(int16_t *)pValue; 736 ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16); 737 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) { 738 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16); 739 return -EINVAL; 740 } else { 741 pDownmixer->type = (downmix_type_t) value16; 742 break; 743 744 default: 745 ALOGE("Downmix_setParameter unknown parameter %" PRId32, param); 746 return -EINVAL; 747 } 748} 749 750 return 0; 751} /* end Downmix_setParameter */ 752 753 754/*---------------------------------------------------------------------------- 755 * Downmix_getParameter() 756 *---------------------------------------------------------------------------- 757 * Purpose: 758 * Get a Downmix parameter 759 * 760 * Inputs: 761 * pDownmixer handle to instance data 762 * param parameter 763 * pValue pointer to variable to hold retrieved value 764 * pSize pointer to value size: maximum size as input 765 * 766 * Outputs: 767 * *pValue updated with parameter value 768 * *pSize updated with actual value size 769 * 770 * Returns: 771 * 0 indicates success 772 * 773 * Side Effects: 774 * 775 *---------------------------------------------------------------------------- 776 */ 777int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) { 778 int16_t *pValue16; 779 780 switch (param) { 781 782 case DOWNMIX_PARAM_TYPE: 783 if (*pSize < sizeof(int16_t)) { 784 ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize); 785 return -EINVAL; 786 } 787 pValue16 = (int16_t *)pValue; 788 *pValue16 = (int16_t) pDownmixer->type; 789 *pSize = sizeof(int16_t); 790 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16); 791 break; 792 793 default: 794 ALOGE("Downmix_getParameter unknown parameter %" PRId16, param); 795 return -EINVAL; 796 } 797 798 return 0; 799} /* end Downmix_getParameter */ 800 801 802/*---------------------------------------------------------------------------- 803 * Downmix_foldFromQuad() 804 *---------------------------------------------------------------------------- 805 * Purpose: 806 * downmix a quad signal to stereo 807 * 808 * Inputs: 809 * pSrc quad audio samples to downmix 810 * numFrames the number of quad frames to downmix 811 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, 812 * or overwrite pDst (when false) 813 * 814 * Outputs: 815 * pDst downmixed stereo audio samples 816 * 817 *---------------------------------------------------------------------------- 818 */ 819void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 820 // sample at index 0 is FL 821 // sample at index 1 is FR 822 // sample at index 2 is RL 823 // sample at index 3 is RR 824 if (accumulate) { 825 while (numFrames) { 826 // FL + RL 827 pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1)); 828 // FR + RR 829 pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1)); 830 pSrc += 4; 831 pDst += 2; 832 numFrames--; 833 } 834 } else { // same code as above but without adding and clamping pDst[i] to itself 835 while (numFrames) { 836 // FL + RL 837 pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1); 838 // FR + RR 839 pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1); 840 pSrc += 4; 841 pDst += 2; 842 numFrames--; 843 } 844 } 845} 846 847 848/*---------------------------------------------------------------------------- 849 * Downmix_foldFrom5Point1() 850 *---------------------------------------------------------------------------- 851 * Purpose: 852 * downmix a 5.1 signal to stereo 853 * 854 * Inputs: 855 * pSrc 5.1 audio samples to downmix 856 * numFrames the number of 5.1 frames to downmix 857 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, 858 * or overwrite pDst (when false) 859 * 860 * Outputs: 861 * pDst downmixed stereo audio samples 862 * 863 *---------------------------------------------------------------------------- 864 */ 865void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 866 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format 867 // sample at index 0 is FL 868 // sample at index 1 is FR 869 // sample at index 2 is FC 870 // sample at index 3 is LFE 871 // sample at index 4 is RL 872 // sample at index 5 is RR 873 // code is mostly duplicated between the two values of accumulate to avoid repeating the test 874 // for every sample 875 if (accumulate) { 876 while (numFrames) { 877 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 878 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 879 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 880 // FL + centerPlusLfeContrib + RL 881 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12); 882 // FR + centerPlusLfeContrib + RR 883 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12); 884 // accumulate in destination 885 pDst[0] = clamp16(pDst[0] + (lt >> 13)); 886 pDst[1] = clamp16(pDst[1] + (rt >> 13)); 887 pSrc += 6; 888 pDst += 2; 889 numFrames--; 890 } 891 } else { // same code as above but without adding and clamping pDst[i] to itself 892 while (numFrames) { 893 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 894 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 895 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 896 // FL + centerPlusLfeContrib + RL 897 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12); 898 // FR + centerPlusLfeContrib + RR 899 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12); 900 // store in destination 901 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above 902 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above 903 pSrc += 6; 904 pDst += 2; 905 numFrames--; 906 } 907 } 908} 909 910 911/*---------------------------------------------------------------------------- 912 * Downmix_foldFrom7Point1() 913 *---------------------------------------------------------------------------- 914 * Purpose: 915 * downmix a 7.1 signal to stereo 916 * 917 * Inputs: 918 * pSrc 7.1 audio samples to downmix 919 * numFrames the number of 7.1 frames to downmix 920 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, 921 * or overwrite pDst (when false) 922 * 923 * Outputs: 924 * pDst downmixed stereo audio samples 925 * 926 *---------------------------------------------------------------------------- 927 */ 928void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 929 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format 930 // sample at index 0 is FL 931 // sample at index 1 is FR 932 // sample at index 2 is FC 933 // sample at index 3 is LFE 934 // sample at index 4 is RL 935 // sample at index 5 is RR 936 // sample at index 6 is SL 937 // sample at index 7 is SR 938 // code is mostly duplicated between the two values of accumulate to avoid repeating the test 939 // for every sample 940 if (accumulate) { 941 while (numFrames) { 942 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 943 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 944 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 945 // FL + centerPlusLfeContrib + SL + RL 946 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12); 947 // FR + centerPlusLfeContrib + SR + RR 948 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12); 949 //accumulate in destination 950 pDst[0] = clamp16(pDst[0] + (lt >> 13)); 951 pDst[1] = clamp16(pDst[1] + (rt >> 13)); 952 pSrc += 8; 953 pDst += 2; 954 numFrames--; 955 } 956 } else { // same code as above but without adding and clamping pDst[i] to itself 957 while (numFrames) { 958 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 959 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 960 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 961 // FL + centerPlusLfeContrib + SL + RL 962 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12); 963 // FR + centerPlusLfeContrib + SR + RR 964 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12); 965 // store in destination 966 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above 967 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above 968 pSrc += 8; 969 pDst += 2; 970 numFrames--; 971 } 972 } 973} 974 975 976/*---------------------------------------------------------------------------- 977 * Downmix_foldGeneric() 978 *---------------------------------------------------------------------------- 979 * Purpose: 980 * downmix to stereo a multichannel signal whose format is: 981 * - has FL/FR 982 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right 983 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right 984 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels 985 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels 986 * Only handles channel masks not enumerated in downmix_input_channel_mask_t 987 * 988 * Inputs: 989 * mask the channel mask of pSrc 990 * pSrc multichannel audio buffer to downmix 991 * numFrames the number of multichannel frames to downmix 992 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, 993 * or overwrite pDst (when false) 994 * 995 * Outputs: 996 * pDst downmixed stereo audio samples 997 * 998 * Returns: false if multichannel format is not supported 999 * 1000 *---------------------------------------------------------------------------- 1001 */ 1002bool Downmix_foldGeneric( 1003 uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 1004 1005 if (!Downmix_validChannelMask(mask)) { 1006 return false; 1007 } 1008 1009 const bool hasSides = (mask & kSides) != 0; 1010 const bool hasBacks = (mask & kBacks) != 0; 1011 1012 const int numChan = audio_channel_count_from_out_mask(mask); 1013 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER); 1014 const bool hasLFE = 1015 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY); 1016 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER); 1017 // compute at what index each channel is: samples will be in the following order: 1018 // FL FR FC LFE BL BR BC SL SR 1019 // when a channel is not present, its index is set to the same as the index of the preceding 1020 // channel 1021 const int indexFC = hasFC ? 2 : 1; // front center 1022 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency 1023 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left 1024 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right 1025 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center 1026 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left 1027 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right 1028 1029 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format 1030 // code is mostly duplicated between the two values of accumulate to avoid repeating the test 1031 // for every sample 1032 if (accumulate) { 1033 while (numFrames) { 1034 // compute contribution of FC, BC and LFE 1035 centersLfeContrib = 0; 1036 if (hasFC) { centersLfeContrib += pSrc[indexFC]; } 1037 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; } 1038 if (hasBC) { centersLfeContrib += pSrc[indexBC]; } 1039 centersLfeContrib *= MINUS_3_DB_IN_Q19_12; 1040 // always has FL/FR 1041 lt = (pSrc[0] << 12); 1042 rt = (pSrc[1] << 12); 1043 // mix in sides and backs 1044 if (hasSides) { 1045 lt += pSrc[indexSL] << 12; 1046 rt += pSrc[indexSR] << 12; 1047 } 1048 if (hasBacks) { 1049 lt += pSrc[indexBL] << 12; 1050 rt += pSrc[indexBR] << 12; 1051 } 1052 lt += centersLfeContrib; 1053 rt += centersLfeContrib; 1054 // accumulate in destination 1055 pDst[0] = clamp16(pDst[0] + (lt >> 13)); 1056 pDst[1] = clamp16(pDst[1] + (rt >> 13)); 1057 pSrc += numChan; 1058 pDst += 2; 1059 numFrames--; 1060 } 1061 } else { 1062 while (numFrames) { 1063 // compute contribution of FC, BC and LFE 1064 centersLfeContrib = 0; 1065 if (hasFC) { centersLfeContrib += pSrc[indexFC]; } 1066 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; } 1067 if (hasBC) { centersLfeContrib += pSrc[indexBC]; } 1068 centersLfeContrib *= MINUS_3_DB_IN_Q19_12; 1069 // always has FL/FR 1070 lt = (pSrc[0] << 12); 1071 rt = (pSrc[1] << 12); 1072 // mix in sides and backs 1073 if (hasSides) { 1074 lt += pSrc[indexSL] << 12; 1075 rt += pSrc[indexSR] << 12; 1076 } 1077 if (hasBacks) { 1078 lt += pSrc[indexBL] << 12; 1079 rt += pSrc[indexBR] << 12; 1080 } 1081 lt += centersLfeContrib; 1082 rt += centersLfeContrib; 1083 // store in destination 1084 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above 1085 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above 1086 pSrc += numChan; 1087 pDst += 2; 1088 numFrames--; 1089 } 1090 } 1091 return true; 1092} 1093