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