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