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