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
33extern "C" {
34
35// effect_handle_t interface implementation for LE effect
36extern const struct effect_interface_s gLEInterface;
37
38// AOSP Loudness Enhancer UUID: fa415329-2034-4bea-b5dc-5b381c8d1e2c
39const 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
50enum le_state_e {
51    LOUDNESS_ENHANCER_STATE_UNINITIALIZED,
52    LOUDNESS_ENHANCER_STATE_INITIALIZED,
53    LOUDNESS_ENHANCER_STATE_ACTIVE,
54};
55
56struct 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
70void 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
83static 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
104int 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
138void 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
156int 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
195int 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
233int 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
250int 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//
269int 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
313int 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 */
432int 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
448const 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")))
457audio_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