Home | History | Annotate | Download | only in loudness
      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