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 29extern "C" { 30 31// effect_handle_t interface implementation for LE effect 32extern const struct effect_interface_s gLEInterface; 33 34// AOSP Loudness Enhancer UUID: fa415329-2034-4bea-b5dc-5b381c8d1e2c 35const 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 46enum le_state_e { 47 LOUDNESS_ENHANCER_STATE_UNINITIALIZED, 48 LOUDNESS_ENHANCER_STATE_INITIALIZED, 49 LOUDNESS_ENHANCER_STATE_ACTIVE, 50}; 51 52struct 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 66void 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 79static 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 100int 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 134void 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 152int 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 191int LELib_Create(const effect_uuid_t *uuid, 192 int32_t sessionId __unused, 193 int32_t ioId __unused, 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 230int 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 247int 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// 266int 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 310int 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 == 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 == 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 || replySize == 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 == 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 */ 430int 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 446const 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"))) 455audio_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