blob: 3c78e95f26d335644409f38f0796004dcbcaff9a [file] [log] [blame]
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -07001/*
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
Mark Salyzyn60d02072016-09-29 08:48:48 -070019
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -070020#include <assert.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070021#include <math.h>
22#include <new>
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -070023#include <stdlib.h>
24#include <string.h>
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -070025#include <time.h>
Mark Salyzyn60d02072016-09-29 08:48:48 -070026
27#include <android/log.h>
28
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -070029#include <audio_effects/effect_loudnessenhancer.h>
30#include "dsp/core/dynamic_range_compression.h"
31
32extern "C" {
33
34// effect_handle_t interface implementation for LE effect
35extern const struct effect_interface_s gLEInterface;
36
37// AOSP Loudness Enhancer UUID: fa415329-2034-4bea-b5dc-5b381c8d1e2c
38const effect_descriptor_t gLEDescriptor = {
39 {0xfe3199be, 0xaed0, 0x413f, 0x87bb, {0x11, 0x26, 0x0e, 0xb6, 0x3c, 0xf1}}, // type
40 {0xfa415329, 0x2034, 0x4bea, 0xb5dc, {0x5b, 0x38, 0x1c, 0x8d, 0x1e, 0x2c}}, // uuid
41 EFFECT_CONTROL_API_VERSION,
42 (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST),
43 0, // TODO
44 1,
45 "Loudness Enhancer",
46 "The Android Open Source Project",
47};
48
49enum le_state_e {
50 LOUDNESS_ENHANCER_STATE_UNINITIALIZED,
51 LOUDNESS_ENHANCER_STATE_INITIALIZED,
52 LOUDNESS_ENHANCER_STATE_ACTIVE,
53};
54
55struct LoudnessEnhancerContext {
56 const struct effect_interface_s *mItfe;
57 effect_config_t mConfig;
58 uint8_t mState;
59 int32_t mTargetGainmB;// target gain in mB
60 // in this implementation, there is no coupling between the compression on the left and right
61 // channels
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -070062 le_fx::AdaptiveDynamicRangeCompression* mCompressor;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -070063};
64
65//
66//--- Local functions (not directly used by effect interface)
67//
68
69void LE_reset(LoudnessEnhancerContext *pContext)
70{
71 ALOGV(" > LE_reset(%p)", pContext);
72
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -070073 if (pContext->mCompressor != NULL) {
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -070074 float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification
75 ALOGV("LE_reset(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp);
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -070076 pContext->mCompressor->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -070077 } else {
78 ALOGE("LE_reset(%p): null compressors, can't apply target gain", pContext);
79 }
80}
81
82static inline int16_t clamp16(int32_t sample)
83{
84 if ((sample>>15) ^ (sample>>31))
85 sample = 0x7FFF ^ (sample>>31);
86 return sample;
87}
88
89//----------------------------------------------------------------------------
90// LE_setConfig()
91//----------------------------------------------------------------------------
92// Purpose: Set input and output audio configuration.
93//
94// Inputs:
95// pContext: effect engine context
96// pConfig: pointer to effect_config_t structure holding input and output
97// configuration parameters
98//
99// Outputs:
100//
101//----------------------------------------------------------------------------
102
103int LE_setConfig(LoudnessEnhancerContext *pContext, effect_config_t *pConfig)
104{
105 ALOGV("LE_setConfig(%p)", pContext);
106
107 if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate) return -EINVAL;
108 if (pConfig->inputCfg.channels != pConfig->outputCfg.channels) return -EINVAL;
109 if (pConfig->inputCfg.format != pConfig->outputCfg.format) return -EINVAL;
110 if (pConfig->inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO) return -EINVAL;
111 if (pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_WRITE &&
112 pConfig->outputCfg.accessMode != EFFECT_BUFFER_ACCESS_ACCUMULATE) return -EINVAL;
113 if (pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT) return -EINVAL;
114
115 pContext->mConfig = *pConfig;
116
117 LE_reset(pContext);
118
119 return 0;
120}
121
122
123//----------------------------------------------------------------------------
124// LE_getConfig()
125//----------------------------------------------------------------------------
126// Purpose: Get input and output audio configuration.
127//
128// Inputs:
129// pContext: effect engine context
130// pConfig: pointer to effect_config_t structure holding input and output
131// configuration parameters
132//
133// Outputs:
134//
135//----------------------------------------------------------------------------
136
137void LE_getConfig(LoudnessEnhancerContext *pContext, effect_config_t *pConfig)
138{
139 *pConfig = pContext->mConfig;
140}
141
142
143//----------------------------------------------------------------------------
144// LE_init()
145//----------------------------------------------------------------------------
146// Purpose: Initialize engine with default configuration.
147//
148// Inputs:
149// pContext: effect engine context
150//
151// Outputs:
152//
153//----------------------------------------------------------------------------
154
155int LE_init(LoudnessEnhancerContext *pContext)
156{
157 ALOGV("LE_init(%p)", pContext);
158
159 pContext->mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
160 pContext->mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
161 pContext->mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
162 pContext->mConfig.inputCfg.samplingRate = 44100;
163 pContext->mConfig.inputCfg.bufferProvider.getBuffer = NULL;
164 pContext->mConfig.inputCfg.bufferProvider.releaseBuffer = NULL;
165 pContext->mConfig.inputCfg.bufferProvider.cookie = NULL;
166 pContext->mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
167 pContext->mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
168 pContext->mConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
169 pContext->mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
170 pContext->mConfig.outputCfg.samplingRate = 44100;
171 pContext->mConfig.outputCfg.bufferProvider.getBuffer = NULL;
172 pContext->mConfig.outputCfg.bufferProvider.releaseBuffer = NULL;
173 pContext->mConfig.outputCfg.bufferProvider.cookie = NULL;
174 pContext->mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
175
176 pContext->mTargetGainmB = LOUDNESS_ENHANCER_DEFAULT_TARGET_GAIN_MB;
177 float targetAmp = pow(10, pContext->mTargetGainmB/2000.0f); // mB to linear amplification
178 ALOGV("LE_init(): Target gain=%dmB <=> factor=%.2fX", pContext->mTargetGainmB, targetAmp);
179
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -0700180 if (pContext->mCompressor == NULL) {
181 pContext->mCompressor = new le_fx::AdaptiveDynamicRangeCompression();
182 pContext->mCompressor->Initialize(targetAmp, pContext->mConfig.inputCfg.samplingRate);
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700183 }
184
185 LE_setConfig(pContext, &pContext->mConfig);
186
187 return 0;
188}
189
190//
191//--- Effect Library Interface Implementation
192//
193
194int LELib_Create(const effect_uuid_t *uuid,
Eric Laurent0f714a42015-06-19 15:33:57 -0700195 int32_t sessionId __unused,
196 int32_t ioId __unused,
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700197 effect_handle_t *pHandle) {
198 ALOGV("LELib_Create()");
199 int ret;
200 int i;
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
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -0700215 pContext->mCompressor = NULL;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700216 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;
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -0700241 if (pContext->mCompressor != NULL) {
242 delete pContext->mCompressor;
243 pContext->mCompressor = NULL;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700244 }
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);
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -0700288 float leftSample, rightSample;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700289 for (inIdx = 0 ; inIdx < inBuffer->frameCount ; inIdx++) {
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -0700290 // 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;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700296 }
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 int retsize;
318
319 if (pContext == NULL || pContext->mState == LOUDNESS_ENHANCER_STATE_UNINITIALIZED) {
320 return -EINVAL;
321 }
322
323// ALOGV("LE_command command %d cmdSize %d",cmdCode, cmdSize);
324 switch (cmdCode) {
325 case EFFECT_CMD_INIT:
326 if (pReplyData == NULL || *replySize != sizeof(int)) {
327 return -EINVAL;
328 }
329 *(int *) pReplyData = LE_init(pContext);
330 break;
331 case EFFECT_CMD_SET_CONFIG:
332 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
Eric Laurent0f714a42015-06-19 15:33:57 -0700333 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700334 return -EINVAL;
335 }
336 *(int *) pReplyData = LE_setConfig(pContext,
337 (effect_config_t *) pCmdData);
338 break;
339 case EFFECT_CMD_GET_CONFIG:
340 if (pReplyData == NULL ||
341 *replySize != sizeof(effect_config_t)) {
342 return -EINVAL;
343 }
344 LE_getConfig(pContext, (effect_config_t *)pReplyData);
345 break;
346 case EFFECT_CMD_RESET:
347 LE_reset(pContext);
348 break;
349 case EFFECT_CMD_ENABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700350 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700351 return -EINVAL;
352 }
353 if (pContext->mState != LOUDNESS_ENHANCER_STATE_INITIALIZED) {
354 return -ENOSYS;
355 }
356 pContext->mState = LOUDNESS_ENHANCER_STATE_ACTIVE;
357 ALOGV("EFFECT_CMD_ENABLE() OK");
358 *(int *)pReplyData = 0;
359 break;
360 case EFFECT_CMD_DISABLE:
361 if (pReplyData == NULL || *replySize != sizeof(int)) {
362 return -EINVAL;
363 }
364 if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) {
365 return -ENOSYS;
366 }
367 pContext->mState = LOUDNESS_ENHANCER_STATE_INITIALIZED;
368 ALOGV("EFFECT_CMD_DISABLE() OK");
369 *(int *)pReplyData = 0;
370 break;
371 case EFFECT_CMD_GET_PARAM: {
372 if (pCmdData == NULL ||
373 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700374 pReplyData == NULL || replySize == NULL ||
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700375 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
376 return -EINVAL;
377 }
378 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
379 effect_param_t *p = (effect_param_t *)pReplyData;
380 p->status = 0;
381 *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
382 if (p->psize != sizeof(uint32_t)) {
383 p->status = -EINVAL;
384 break;
385 }
386 switch (*(uint32_t *)p->data) {
387 case LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB:
388 ALOGV("get target gain(mB) = %d", pContext->mTargetGainmB);
389 *((int32_t *)p->data + 1) = pContext->mTargetGainmB;
390 p->vsize = sizeof(int32_t);
391 *replySize += sizeof(int32_t);
392 break;
393 default:
394 p->status = -EINVAL;
395 }
396 } break;
397 case EFFECT_CMD_SET_PARAM: {
398 if (pCmdData == NULL ||
399 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700400 pReplyData == NULL || replySize == NULL || *replySize != sizeof(int32_t)) {
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700401 return -EINVAL;
402 }
403 *(int32_t *)pReplyData = 0;
404 effect_param_t *p = (effect_param_t *)pCmdData;
405 if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) {
406 *(int32_t *)pReplyData = -EINVAL;
407 break;
408 }
409 switch (*(uint32_t *)p->data) {
410 case LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB:
411 pContext->mTargetGainmB = *((int32_t *)p->data + 1);
412 ALOGV("set target gain(mB) = %d", pContext->mTargetGainmB);
413 LE_reset(pContext); // apply parameter update
414 break;
415 default:
416 *(int32_t *)pReplyData = -EINVAL;
417 }
418 } break;
419 case EFFECT_CMD_SET_DEVICE:
420 case EFFECT_CMD_SET_VOLUME:
421 case EFFECT_CMD_SET_AUDIO_MODE:
422 break;
423
424 default:
425 ALOGW("LE_command invalid command %d",cmdCode);
426 return -EINVAL;
427 }
428
429 return 0;
430}
431
432/* Effect Control Interface Implementation: get_descriptor */
433int LE_getDescriptor(effect_handle_t self,
434 effect_descriptor_t *pDescriptor)
435{
436 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *) self;
437
438 if (pContext == NULL || pDescriptor == NULL) {
439 ALOGV("LE_getDescriptor() invalid param");
440 return -EINVAL;
441 }
442
443 *pDescriptor = gLEDescriptor;
444
445 return 0;
446} /* end LE_getDescriptor */
447
448// effect_handle_t interface implementation for DRC effect
449const struct effect_interface_s gLEInterface = {
450 LE_process,
451 LE_command,
452 LE_getDescriptor,
453 NULL,
454};
455
456// This is the only symbol that needs to be exported
457__attribute__ ((visibility ("default")))
458audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergy dev9803acb2013-12-17 17:48:51 -0800459 .tag = AUDIO_EFFECT_LIBRARY_TAG,
460 .version = EFFECT_LIBRARY_API_VERSION,
461 .name = "Loudness Enhancer Library",
462 .implementor = "The Android Open Source Project",
463 .create_effect = LELib_Create,
464 .release_effect = LELib_Release,
465 .get_descriptor = LELib_GetDescriptor,
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700466};
467
468}; // extern "C"
469