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