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 #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