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