1/* 2 * Copyright (C) 2013 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 "EffectLE" 18//#define LOG_NDEBUG 0 19 20#include <assert.h> 21#include <math.h> 22#include <stdlib.h> 23#include <string.h> 24#include <time.h> 25 26#include <new> 27 28#include <log/log.h> 29 30#include <audio_effects/effect_loudnessenhancer.h> 31#include "dsp/core/dynamic_range_compression.h" 32 33extern "C" { 34 35// effect_handle_t interface implementation for LE effect 36extern const struct effect_interface_s gLEInterface; 37 38// AOSP Loudness Enhancer UUID: fa415329-2034-4bea-b5dc-5b381c8d1e2c 39const effect_descriptor_t gLEDescriptor = { 40 {0xfe3199be, 0xaed0, 0x413f, 0x87bb, {0x11, 0x26, 0x0e, 0xb6, 0x3c, 0xf1}}, // type 41 {0xfa415329, 0x2034, 0x4bea, 0xb5dc, {0x5b, 0x38, 0x1c, 0x8d, 0x1e, 0x2c}}, // uuid 42 EFFECT_CONTROL_API_VERSION, 43 (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST), 44 0, // TODO 45 1, 46 "Loudness Enhancer", 47 "The Android Open Source Project", 48}; 49 50enum le_state_e { 51 LOUDNESS_ENHANCER_STATE_UNINITIALIZED, 52 LOUDNESS_ENHANCER_STATE_INITIALIZED, 53 LOUDNESS_ENHANCER_STATE_ACTIVE, 54}; 55 56struct LoudnessEnhancerContext { 57 const struct effect_interface_s *mItfe; 58 effect_config_t mConfig; 59 uint8_t mState; 60 int32_t mTargetGainmB;// target gain in mB 61 // in this implementation, there is no coupling between the compression on the left and right 62 // channels 63 le_fx::AdaptiveDynamicRangeCompression* mCompressor; 64}; 65 66// 67//--- Local functions (not directly used by effect interface) 68// 69 70void LE_reset(LoudnessEnhancerContext *pContext) 71{ 72 ALOGV(" > LE_reset(%p)", pContext); 73 74 if (pContext->mCompressor != NULL) { 75 float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification 76 ALOGV("LE_reset(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp); 77 pContext->mCompressor->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); 78 } else { 79 ALOGE("LE_reset(%p): null compressors, can't apply target gain", pContext); 80 } 81} 82 83static inline int16_t clamp16(int32_t sample) 84{ 85 if ((sample>>15) ^ (sample>>31)) 86 sample = 0x7FFF ^ (sample>>31); 87 return sample; 88} 89 90//---------------------------------------------------------------------------- 91// LE_setConfig() 92//---------------------------------------------------------------------------- 93// Purpose: Set input and output audio configuration. 94// 95// Inputs: 96// pContext: effect engine context 97// pConfig: pointer to effect_config_t structure holding input and output 98// configuration parameters 99// 100// Outputs: 101// 102//---------------------------------------------------------------------------- 103 104int LE_setConfig(LoudnessEnhancerContext *pContext, effect_config_t *pConfig) 105{ 106 ALOGV("LE_setConfig(%p)", pContext); 107 108 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL; 109 if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL; 110 if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL; 111 if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL; 112 if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE && 113 pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL; 114 if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL; 115 116 pContext->mConfig = *pConfig; 117 118 LE_reset(pContext); 119 120 return 0; 121} 122 123 124//---------------------------------------------------------------------------- 125// LE_getConfig() 126//---------------------------------------------------------------------------- 127// Purpose: Get input and output audio configuration. 128// 129// Inputs: 130// pContext: effect engine context 131// pConfig: pointer to effect_config_t structure holding input and output 132// configuration parameters 133// 134// Outputs: 135// 136//---------------------------------------------------------------------------- 137 138void LE_getConfig(LoudnessEnhancerContext *pContext, effect_config_t *pConfig) 139{ 140 *pConfig = pContext->mConfig; 141} 142 143 144//---------------------------------------------------------------------------- 145// LE_init() 146//---------------------------------------------------------------------------- 147// Purpose: Initialize engine with default configuration. 148// 149// Inputs: 150// pContext: effect engine context 151// 152// Outputs: 153// 154//---------------------------------------------------------------------------- 155 156int LE_init(LoudnessEnhancerContext *pContext) 157{ 158 ALOGV("LE_init(%p)", pContext); 159 160 pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; 161 pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; 162 pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; 163 pContext->mConfig.inputCfg.samplingRate = 44100; 164 pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL; 165 pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL; 166 pContext->mConfig.inputCfg.bufferProvider.cookie = NULL; 167 pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL; 168 pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; 169 pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; 170 pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; 171 pContext->mConfig.outputCfg.samplingRate = 44100; 172 pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL; 173 pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL; 174 pContext->mConfig.outputCfg.bufferProvider.cookie = NULL; 175 pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL; 176 177 pContext->mTargetGainmB = LOUDNESS_ENHANCER_DEFAULT_TARGET_GAIN_MB; 178 float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification 179 ALOGV("LE_init(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp); 180 181 if (pContext->mCompressor == NULL) { 182 pContext->mCompressor = new le_fx::AdaptiveDynamicRangeCompression(); 183 pContext->mCompressor->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate); 184 } 185 186 LE_setConfig(pContext, &pContext->mConfig); 187 188 return 0; 189} 190 191// 192//--- Effect Library Interface Implementation 193// 194 195int LELib_Create(const effect_uuid_t *uuid, 196 int32_t sessionId __unused, 197 int32_t ioId __unused, 198 effect_handle_t *pHandle) { 199 ALOGV("LELib_Create()"); 200 int ret; 201 202 if (pHandle == NULL || uuid == NULL) { 203 return -EINVAL; 204 } 205 206 if (memcmp(uuid, &gLEDescriptor.uuid, sizeof(effect_uuid_t)) != 0) { 207 return -EINVAL; 208 } 209 210 LoudnessEnhancerContext *pContext = new LoudnessEnhancerContext; 211 212 pContext->mItfe = &gLEInterface; 213 pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED; 214 215 pContext->mCompressor = NULL; 216 ret = LE_init(pContext); 217 if (ret < 0) { 218 ALOGW("LELib_Create() init failed"); 219 delete pContext; 220 return ret; 221 } 222 223 *pHandle = (effect_handle_t)pContext; 224 225 pContext->mState = LOUDNESS_ENHANCER_STATE_INITIALIZED; 226 227 ALOGV(" LELib_Create context is %p", pContext); 228 229 return 0; 230 231} 232 233int LELib_Release(effect_handle_t handle) { 234 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)handle; 235 236 ALOGV("LELib_Release %p", handle); 237 if (pContext == NULL) { 238 return -EINVAL; 239 } 240 pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED; 241 if (pContext->mCompressor != NULL) { 242 delete pContext->mCompressor; 243 pContext->mCompressor = NULL; 244 } 245 delete pContext; 246 247 return 0; 248} 249 250int LELib_GetDescriptor(const effect_uuid_t *uuid, 251 effect_descriptor_t *pDescriptor) { 252 253 if (pDescriptor == NULL || uuid == NULL){ 254 ALOGV("LELib_GetDescriptor() called with NULL pointer"); 255 return -EINVAL; 256 } 257 258 if (memcmp(uuid, &gLEDescriptor.uuid, sizeof(effect_uuid_t)) == 0) { 259 *pDescriptor = gLEDescriptor; 260 return 0; 261 } 262 263 return -EINVAL; 264} /* end LELib_GetDescriptor */ 265 266// 267//--- Effect Control Interface Implementation 268// 269int LE_process( 270 effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) 271{ 272 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)self; 273 274 if (pContext == NULL) { 275 return -EINVAL; 276 } 277 278 if (inBuffer == NULL || inBuffer->raw == NULL || 279 outBuffer == NULL || outBuffer->raw == NULL || 280 inBuffer->frameCount != outBuffer->frameCount || 281 inBuffer->frameCount == 0) { 282 return -EINVAL; 283 } 284 285 //ALOGV("LE about to process %d samples", inBuffer->frameCount); 286 uint16_t inIdx; 287 float inputAmp = pow(10, pContext->mTargetGainmB/2000.0f); 288 float leftSample, rightSample; 289 for (inIdx = 0 ; inIdx < inBuffer->frameCount ; inIdx++) { 290 // makeup gain is applied on the input of the compressor 291 leftSample = inputAmp * (float)inBuffer->s16[2*inIdx]; 292 rightSample = inputAmp * (float)inBuffer->s16[2*inIdx +1]; 293 pContext->mCompressor->Compress(&leftSample, &rightSample); 294 inBuffer->s16[2*inIdx] = (int16_t) leftSample; 295 inBuffer->s16[2*inIdx +1] = (int16_t) rightSample; 296 } 297 298 if (inBuffer->raw != outBuffer->raw) { 299 if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) { 300 for (size_t i = 0; i < outBuffer->frameCount*2; i++) { 301 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]); 302 } 303 } else { 304 memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t)); 305 } 306 } 307 if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) { 308 return -ENODATA; 309 } 310 return 0; 311} 312 313int LE_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize, 314 void *pCmdData, uint32_t *replySize, void *pReplyData) { 315 316 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)self; 317 318 if (pContext == NULL || pContext->mState == LOUDNESS_ENHANCER_STATE_UNINITIALIZED) { 319 return -EINVAL; 320 } 321 322// ALOGV("LE_command command %d cmdSize %d",cmdCode, cmdSize); 323 switch (cmdCode) { 324 case EFFECT_CMD_INIT: 325 if (pReplyData == NULL || *replySize != sizeof(int)) { 326 return -EINVAL; 327 } 328 *(int *) pReplyData = LE_init(pContext); 329 break; 330 case EFFECT_CMD_SET_CONFIG: 331 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t) 332 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) { 333 return -EINVAL; 334 } 335 *(int *) pReplyData = LE_setConfig(pContext, 336 (effect_config_t *) pCmdData); 337 break; 338 case EFFECT_CMD_GET_CONFIG: 339 if (pReplyData == NULL || 340 *replySize != sizeof(effect_config_t)) { 341 return -EINVAL; 342 } 343 LE_getConfig(pContext, (effect_config_t *)pReplyData); 344 break; 345 case EFFECT_CMD_RESET: 346 LE_reset(pContext); 347 break; 348 case EFFECT_CMD_ENABLE: 349 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) { 350 return -EINVAL; 351 } 352 if (pContext->mState != LOUDNESS_ENHANCER_STATE_INITIALIZED) { 353 return -ENOSYS; 354 } 355 pContext->mState = LOUDNESS_ENHANCER_STATE_ACTIVE; 356 ALOGV("EFFECT_CMD_ENABLE() OK"); 357 *(int *)pReplyData = 0; 358 break; 359 case EFFECT_CMD_DISABLE: 360 if (pReplyData == NULL || *replySize != sizeof(int)) { 361 return -EINVAL; 362 } 363 if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) { 364 return -ENOSYS; 365 } 366 pContext->mState = LOUDNESS_ENHANCER_STATE_INITIALIZED; 367 ALOGV("EFFECT_CMD_DISABLE() OK"); 368 *(int *)pReplyData = 0; 369 break; 370 case EFFECT_CMD_GET_PARAM: { 371 if (pCmdData == NULL || 372 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) || 373 pReplyData == NULL || replySize == NULL || 374 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) { 375 return -EINVAL; 376 } 377 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t)); 378 effect_param_t *p = (effect_param_t *)pReplyData; 379 p->status = 0; 380 *replySize = sizeof(effect_param_t) + sizeof(uint32_t); 381 if (p->psize != sizeof(uint32_t)) { 382 p->status = -EINVAL; 383 break; 384 } 385 switch (*(uint32_t *)p->data) { 386 case LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB: 387 ALOGV("get target gain(mB) = %d", pContext->mTargetGainmB); 388 *((int32_t *)p->data + 1) = pContext->mTargetGainmB; 389 p->vsize = sizeof(int32_t); 390 *replySize += sizeof(int32_t); 391 break; 392 default: 393 p->status = -EINVAL; 394 } 395 } break; 396 case EFFECT_CMD_SET_PARAM: { 397 if (pCmdData == NULL || 398 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) || 399 pReplyData == NULL || replySize == NULL || *replySize != sizeof(int32_t)) { 400 return -EINVAL; 401 } 402 *(int32_t *)pReplyData = 0; 403 effect_param_t *p = (effect_param_t *)pCmdData; 404 if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) { 405 *(int32_t *)pReplyData = -EINVAL; 406 break; 407 } 408 switch (*(uint32_t *)p->data) { 409 case LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB: 410 pContext->mTargetGainmB = *((int32_t *)p->data + 1); 411 ALOGV("set target gain(mB) = %d", pContext->mTargetGainmB); 412 LE_reset(pContext); // apply parameter update 413 break; 414 default: 415 *(int32_t *)pReplyData = -EINVAL; 416 } 417 } break; 418 case EFFECT_CMD_SET_DEVICE: 419 case EFFECT_CMD_SET_VOLUME: 420 case EFFECT_CMD_SET_AUDIO_MODE: 421 break; 422 423 default: 424 ALOGW("LE_command invalid command %d",cmdCode); 425 return -EINVAL; 426 } 427 428 return 0; 429} 430 431/* Effect Control Interface Implementation: get_descriptor */ 432int LE_getDescriptor(effect_handle_t self, 433 effect_descriptor_t *pDescriptor) 434{ 435 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *) self; 436 437 if (pContext == NULL || pDescriptor == NULL) { 438 ALOGV("LE_getDescriptor() invalid param"); 439 return -EINVAL; 440 } 441 442 *pDescriptor = gLEDescriptor; 443 444 return 0; 445} /* end LE_getDescriptor */ 446 447// effect_handle_t interface implementation for DRC effect 448const struct effect_interface_s gLEInterface = { 449 LE_process, 450 LE_command, 451 LE_getDescriptor, 452 NULL, 453}; 454 455// This is the only symbol that needs to be exported 456__attribute__ ((visibility ("default"))) 457audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { 458 .tag = AUDIO_EFFECT_LIBRARY_TAG, 459 .version = EFFECT_LIBRARY_API_VERSION, 460 .name = "Loudness Enhancer Library", 461 .implementor = "The Android Open Source Project", 462 .create_effect = LELib_Create, 463 .release_effect = LELib_Release, 464 .get_descriptor = LELib_GetDescriptor, 465}; 466 467}; // extern "C" 468 469