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 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 __unused, 153 int32_t ioId __unused, 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 == 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 == 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 || replySize == 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 == 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 == 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 == 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 = 633 audio_channel_count_from_out_mask(pConfig->inputCfg.channels); 634 } 635 636 Downmix_Reset(pDownmixer, init); 637 638 return 0; 639} 640 641 642/*---------------------------------------------------------------------------- 643 * Downmix_Reset() 644 *---------------------------------------------------------------------------- 645 * Purpose: 646 * Reset internal states. 647 * 648 * Inputs: 649 * pDownmixer pointer to downmix context 650 * init true if called from init function 651 * 652 * Outputs: 653* 654 * Returns: 655 * 0 indicates success 656 * 657 * Side Effects: 658 * 659 *---------------------------------------------------------------------------- 660 */ 661 662int Downmix_Reset(downmix_object_t *pDownmixer __unused, bool init __unused) { 663 // nothing to do here 664 return 0; 665} 666 667 668/*---------------------------------------------------------------------------- 669 * Downmix_setParameter() 670 *---------------------------------------------------------------------------- 671 * Purpose: 672 * Set a Downmix parameter 673 * 674 * Inputs: 675 * pDownmixer handle to instance data 676 * param parameter 677 * pValue pointer to parameter value 678 * size value size 679 * 680 * Outputs: 681 * 682 * Returns: 683 * 0 indicates success 684 * 685 * Side Effects: 686 * 687 *---------------------------------------------------------------------------- 688 */ 689int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t size, void *pValue) { 690 691 int16_t value16; 692 ALOGV("Downmix_setParameter, context %p, param %" PRId32 ", value16 %" PRId16 ", value32 %" PRId32, 693 pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue); 694 695 switch (param) { 696 697 case DOWNMIX_PARAM_TYPE: 698 if (size != sizeof(downmix_type_t)) { 699 ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %" PRIu32 ", should be %zu", 700 size, sizeof(downmix_type_t)); 701 return -EINVAL; 702 } 703 value16 = *(int16_t *)pValue; 704 ALOGV("set DOWNMIX_PARAM_TYPE, type %" PRId16, value16); 705 if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 <= DOWNMIX_TYPE_LAST))) { 706 ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %" PRId16, value16); 707 return -EINVAL; 708 } else { 709 pDownmixer->type = (downmix_type_t) value16; 710 break; 711 712 default: 713 ALOGE("Downmix_setParameter unknown parameter %" PRId32, param); 714 return -EINVAL; 715 } 716} 717 718 return 0; 719} /* end Downmix_setParameter */ 720 721 722/*---------------------------------------------------------------------------- 723 * Downmix_getParameter() 724 *---------------------------------------------------------------------------- 725 * Purpose: 726 * Get a Downmix parameter 727 * 728 * Inputs: 729 * pDownmixer handle to instance data 730 * param parameter 731 * pValue pointer to variable to hold retrieved value 732 * pSize pointer to value size: maximum size as input 733 * 734 * Outputs: 735 * *pValue updated with parameter value 736 * *pSize updated with actual value size 737 * 738 * Returns: 739 * 0 indicates success 740 * 741 * Side Effects: 742 * 743 *---------------------------------------------------------------------------- 744 */ 745int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, uint32_t *pSize, void *pValue) { 746 int16_t *pValue16; 747 748 switch (param) { 749 750 case DOWNMIX_PARAM_TYPE: 751 if (*pSize < sizeof(int16_t)) { 752 ALOGE("Downmix_getParameter invalid parameter size %" PRIu32 " for DOWNMIX_PARAM_TYPE", *pSize); 753 return -EINVAL; 754 } 755 pValue16 = (int16_t *)pValue; 756 *pValue16 = (int16_t) pDownmixer->type; 757 *pSize = sizeof(int16_t); 758 ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %" PRId16, *pValue16); 759 break; 760 761 default: 762 ALOGE("Downmix_getParameter unknown parameter %" PRId16, param); 763 return -EINVAL; 764 } 765 766 return 0; 767} /* end Downmix_getParameter */ 768 769 770/*---------------------------------------------------------------------------- 771 * Downmix_foldFromQuad() 772 *---------------------------------------------------------------------------- 773 * Purpose: 774 * downmix a quad signal to stereo 775 * 776 * Inputs: 777 * pSrc quad audio samples to downmix 778 * numFrames the number of quad frames to downmix 779 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, 780 * or overwrite pDst (when false) 781 * 782 * Outputs: 783 * pDst downmixed stereo audio samples 784 * 785 *---------------------------------------------------------------------------- 786 */ 787void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 788 // sample at index 0 is FL 789 // sample at index 1 is FR 790 // sample at index 2 is RL 791 // sample at index 3 is RR 792 if (accumulate) { 793 while (numFrames) { 794 // FL + RL 795 pDst[0] = clamp16(pDst[0] + ((pSrc[0] + pSrc[2]) >> 1)); 796 // FR + RR 797 pDst[1] = clamp16(pDst[1] + ((pSrc[1] + pSrc[3]) >> 1)); 798 pSrc += 4; 799 pDst += 2; 800 numFrames--; 801 } 802 } else { // same code as above but without adding and clamping pDst[i] to itself 803 while (numFrames) { 804 // FL + RL 805 pDst[0] = clamp16((pSrc[0] + pSrc[2]) >> 1); 806 // FR + RR 807 pDst[1] = clamp16((pSrc[1] + pSrc[3]) >> 1); 808 pSrc += 4; 809 pDst += 2; 810 numFrames--; 811 } 812 } 813} 814 815 816/*---------------------------------------------------------------------------- 817 * Downmix_foldFrom5Point1() 818 *---------------------------------------------------------------------------- 819 * Purpose: 820 * downmix a 5.1 signal to stereo 821 * 822 * Inputs: 823 * pSrc 5.1 audio samples to downmix 824 * numFrames the number of 5.1 frames to downmix 825 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, 826 * or overwrite pDst (when false) 827 * 828 * Outputs: 829 * pDst downmixed stereo audio samples 830 * 831 *---------------------------------------------------------------------------- 832 */ 833void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 834 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format 835 // sample at index 0 is FL 836 // sample at index 1 is FR 837 // sample at index 2 is FC 838 // sample at index 3 is LFE 839 // sample at index 4 is RL 840 // sample at index 5 is RR 841 // code is mostly duplicated between the two values of accumulate to avoid repeating the test 842 // for every sample 843 if (accumulate) { 844 while (numFrames) { 845 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 846 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 847 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 848 // FL + centerPlusLfeContrib + RL 849 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12); 850 // FR + centerPlusLfeContrib + RR 851 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12); 852 // accumulate in destination 853 pDst[0] = clamp16(pDst[0] + (lt >> 13)); 854 pDst[1] = clamp16(pDst[1] + (rt >> 13)); 855 pSrc += 6; 856 pDst += 2; 857 numFrames--; 858 } 859 } else { // same code as above but without adding and clamping pDst[i] to itself 860 while (numFrames) { 861 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 862 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 863 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 864 // FL + centerPlusLfeContrib + RL 865 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12); 866 // FR + centerPlusLfeContrib + RR 867 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12); 868 // store in destination 869 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above 870 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above 871 pSrc += 6; 872 pDst += 2; 873 numFrames--; 874 } 875 } 876} 877 878 879/*---------------------------------------------------------------------------- 880 * Downmix_foldFrom7Point1() 881 *---------------------------------------------------------------------------- 882 * Purpose: 883 * downmix a 7.1 signal to stereo 884 * 885 * Inputs: 886 * pSrc 7.1 audio samples to downmix 887 * numFrames the number of 7.1 frames to downmix 888 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, 889 * or overwrite pDst (when false) 890 * 891 * Outputs: 892 * pDst downmixed stereo audio samples 893 * 894 *---------------------------------------------------------------------------- 895 */ 896void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 897 int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format 898 // sample at index 0 is FL 899 // sample at index 1 is FR 900 // sample at index 2 is FC 901 // sample at index 3 is LFE 902 // sample at index 4 is RL 903 // sample at index 5 is RR 904 // sample at index 6 is SL 905 // sample at index 7 is SR 906 // code is mostly duplicated between the two values of accumulate to avoid repeating the test 907 // for every sample 908 if (accumulate) { 909 while (numFrames) { 910 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 911 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 912 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 913 // FL + centerPlusLfeContrib + SL + RL 914 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12); 915 // FR + centerPlusLfeContrib + SR + RR 916 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12); 917 //accumulate in destination 918 pDst[0] = clamp16(pDst[0] + (lt >> 13)); 919 pDst[1] = clamp16(pDst[1] + (rt >> 13)); 920 pSrc += 8; 921 pDst += 2; 922 numFrames--; 923 } 924 } else { // same code as above but without adding and clamping pDst[i] to itself 925 while (numFrames) { 926 // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB) 927 centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) 928 + (pSrc[3] * MINUS_3_DB_IN_Q19_12); 929 // FL + centerPlusLfeContrib + SL + RL 930 lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12); 931 // FR + centerPlusLfeContrib + SR + RR 932 rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12); 933 // store in destination 934 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above 935 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above 936 pSrc += 8; 937 pDst += 2; 938 numFrames--; 939 } 940 } 941} 942 943 944/*---------------------------------------------------------------------------- 945 * Downmix_foldGeneric() 946 *---------------------------------------------------------------------------- 947 * Purpose: 948 * downmix to stereo a multichannel signal whose format is: 949 * - has FL/FR 950 * - if using AUDIO_CHANNEL_OUT_SIDE*, it contains both left and right 951 * - if using AUDIO_CHANNEL_OUT_BACK*, it contains both left and right 952 * - doesn't use any of the AUDIO_CHANNEL_OUT_TOP* channels 953 * - doesn't use any of the AUDIO_CHANNEL_OUT_FRONT_*_OF_CENTER channels 954 * Only handles channel masks not enumerated in downmix_input_channel_mask_t 955 * 956 * Inputs: 957 * mask the channel mask of pSrc 958 * pSrc multichannel audio buffer to downmix 959 * numFrames the number of multichannel frames to downmix 960 * accumulate whether to mix (when true) the result of the downmix with the contents of pDst, 961 * or overwrite pDst (when false) 962 * 963 * Outputs: 964 * pDst downmixed stereo audio samples 965 * 966 * Returns: false if multichannel format is not supported 967 * 968 *---------------------------------------------------------------------------- 969 */ 970bool Downmix_foldGeneric( 971 uint32_t mask, int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) { 972 // check against unsupported channels 973 if (mask & kUnsupported) { 974 ALOGE("Unsupported channels (top or front left/right of center)"); 975 return false; 976 } 977 // verify has FL/FR 978 if ((mask & AUDIO_CHANNEL_OUT_STEREO) != AUDIO_CHANNEL_OUT_STEREO) { 979 ALOGE("Front channels must be present"); 980 return false; 981 } 982 // verify uses SIDE as a pair (ok if not using SIDE at all) 983 bool hasSides = false; 984 if ((mask & kSides) != 0) { 985 if ((mask & kSides) != kSides) { 986 ALOGE("Side channels must be used as a pair"); 987 return false; 988 } 989 hasSides = true; 990 } 991 // verify uses BACK as a pair (ok if not using BACK at all) 992 bool hasBacks = false; 993 if ((mask & kBacks) != 0) { 994 if ((mask & kBacks) != kBacks) { 995 ALOGE("Back channels must be used as a pair"); 996 return false; 997 } 998 hasBacks = true; 999 } 1000 1001 const int numChan = audio_channel_count_from_out_mask(mask); 1002 const bool hasFC = ((mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) == AUDIO_CHANNEL_OUT_FRONT_CENTER); 1003 const bool hasLFE = 1004 ((mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY); 1005 const bool hasBC = ((mask & AUDIO_CHANNEL_OUT_BACK_CENTER) == AUDIO_CHANNEL_OUT_BACK_CENTER); 1006 // compute at what index each channel is: samples will be in the following order: 1007 // FL FR FC LFE BL BR BC SL SR 1008 // when a channel is not present, its index is set to the same as the index of the preceding 1009 // channel 1010 const int indexFC = hasFC ? 2 : 1; // front center 1011 const int indexLFE = hasLFE ? indexFC + 1 : indexFC; // low frequency 1012 const int indexBL = hasBacks ? indexLFE + 1 : indexLFE; // back left 1013 const int indexBR = hasBacks ? indexBL + 1 : indexBL; // back right 1014 const int indexBC = hasBC ? indexBR + 1 : indexBR; // back center 1015 const int indexSL = hasSides ? indexBC + 1 : indexBC; // side left 1016 const int indexSR = hasSides ? indexSL + 1 : indexSL; // side right 1017 1018 int32_t lt, rt, centersLfeContrib; // samples in Q19.12 format 1019 // code is mostly duplicated between the two values of accumulate to avoid repeating the test 1020 // for every sample 1021 if (accumulate) { 1022 while (numFrames) { 1023 // compute contribution of FC, BC and LFE 1024 centersLfeContrib = 0; 1025 if (hasFC) { centersLfeContrib += pSrc[indexFC]; } 1026 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; } 1027 if (hasBC) { centersLfeContrib += pSrc[indexBC]; } 1028 centersLfeContrib *= MINUS_3_DB_IN_Q19_12; 1029 // always has FL/FR 1030 lt = (pSrc[0] << 12); 1031 rt = (pSrc[1] << 12); 1032 // mix in sides and backs 1033 if (hasSides) { 1034 lt += pSrc[indexSL] << 12; 1035 rt += pSrc[indexSR] << 12; 1036 } 1037 if (hasBacks) { 1038 lt += pSrc[indexBL] << 12; 1039 rt += pSrc[indexBR] << 12; 1040 } 1041 lt += centersLfeContrib; 1042 rt += centersLfeContrib; 1043 // accumulate in destination 1044 pDst[0] = clamp16(pDst[0] + (lt >> 13)); 1045 pDst[1] = clamp16(pDst[1] + (rt >> 13)); 1046 pSrc += numChan; 1047 pDst += 2; 1048 numFrames--; 1049 } 1050 } else { 1051 while (numFrames) { 1052 // compute contribution of FC, BC and LFE 1053 centersLfeContrib = 0; 1054 if (hasFC) { centersLfeContrib += pSrc[indexFC]; } 1055 if (hasLFE) { centersLfeContrib += pSrc[indexLFE]; } 1056 if (hasBC) { centersLfeContrib += pSrc[indexBC]; } 1057 centersLfeContrib *= MINUS_3_DB_IN_Q19_12; 1058 // always has FL/FR 1059 lt = (pSrc[0] << 12); 1060 rt = (pSrc[1] << 12); 1061 // mix in sides and backs 1062 if (hasSides) { 1063 lt += pSrc[indexSL] << 12; 1064 rt += pSrc[indexSR] << 12; 1065 } 1066 if (hasBacks) { 1067 lt += pSrc[indexBL] << 12; 1068 rt += pSrc[indexBR] << 12; 1069 } 1070 lt += centersLfeContrib; 1071 rt += centersLfeContrib; 1072 // store in destination 1073 pDst[0] = clamp16(lt >> 13); // differs from when accumulate is true above 1074 pDst[1] = clamp16(rt >> 13); // differs from when accumulate is true above 1075 pSrc += numChan; 1076 pDst += 2; 1077 numFrames--; 1078 } 1079 } 1080 return true; 1081} 1082