blob: 19d408d206f9bf08892cc03ae3f45979dcb5d96c [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;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700200
201 if (pHandle == NULL || uuid == NULL) {
202 return -EINVAL;
203 }
204
205 if (memcmp(uuid, &gLEDescriptor.uuid, sizeof(effect_uuid_t)) != 0) {
206 return -EINVAL;
207 }
208
209 LoudnessEnhancerContext *pContext = new LoudnessEnhancerContext;
210
211 pContext->mItfe = &gLEInterface;
212 pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED;
213
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -0700214 pContext->mCompressor = NULL;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700215 ret = LE_init(pContext);
216 if (ret < 0) {
217 ALOGW("LELib_Create() init failed");
218 delete pContext;
219 return ret;
220 }
221
222 *pHandle = (effect_handle_t)pContext;
223
224 pContext->mState = LOUDNESS_ENHANCER_STATE_INITIALIZED;
225
226 ALOGV(" LELib_Create context is %p", pContext);
227
228 return 0;
229
230}
231
232int LELib_Release(effect_handle_t handle) {
233 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)handle;
234
235 ALOGV("LELib_Release %p", handle);
236 if (pContext == NULL) {
237 return -EINVAL;
238 }
239 pContext->mState = LOUDNESS_ENHANCER_STATE_UNINITIALIZED;
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -0700240 if (pContext->mCompressor != NULL) {
241 delete pContext->mCompressor;
242 pContext->mCompressor = NULL;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700243 }
244 delete pContext;
245
246 return 0;
247}
248
249int LELib_GetDescriptor(const effect_uuid_t *uuid,
250 effect_descriptor_t *pDescriptor) {
251
252 if (pDescriptor == NULL || uuid == NULL){
253 ALOGV("LELib_GetDescriptor() called with NULL pointer");
254 return -EINVAL;
255 }
256
257 if (memcmp(uuid, &gLEDescriptor.uuid, sizeof(effect_uuid_t)) == 0) {
258 *pDescriptor = gLEDescriptor;
259 return 0;
260 }
261
262 return -EINVAL;
263} /* end LELib_GetDescriptor */
264
265//
266//--- Effect Control Interface Implementation
267//
268int LE_process(
269 effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
270{
271 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)self;
272
273 if (pContext == NULL) {
274 return -EINVAL;
275 }
276
277 if (inBuffer == NULL || inBuffer->raw == NULL ||
278 outBuffer == NULL || outBuffer->raw == NULL ||
279 inBuffer->frameCount != outBuffer->frameCount ||
280 inBuffer->frameCount == 0) {
281 return -EINVAL;
282 }
283
284 //ALOGV("LE about to process %d samples", inBuffer->frameCount);
285 uint16_t inIdx;
286 float inputAmp = pow(10, pContext->mTargetGainmB/2000.0f);
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -0700287 float leftSample, rightSample;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700288 for (inIdx = 0 ; inIdx < inBuffer->frameCount ; inIdx++) {
Jean-Michel Trivicd0c4682013-09-25 18:43:55 -0700289 // makeup gain is applied on the input of the compressor
290 leftSample = inputAmp * (float)inBuffer->s16[2*inIdx];
291 rightSample = inputAmp * (float)inBuffer->s16[2*inIdx +1];
292 pContext->mCompressor->Compress(&leftSample, &rightSample);
293 inBuffer->s16[2*inIdx] = (int16_t) leftSample;
294 inBuffer->s16[2*inIdx +1] = (int16_t) rightSample;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700295 }
296
297 if (inBuffer->raw != outBuffer->raw) {
298 if (pContext->mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
299 for (size_t i = 0; i < outBuffer->frameCount*2; i++) {
300 outBuffer->s16[i] = clamp16(outBuffer->s16[i] + inBuffer->s16[i]);
301 }
302 } else {
303 memcpy(outBuffer->raw, inBuffer->raw, outBuffer->frameCount * 2 * sizeof(int16_t));
304 }
305 }
306 if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) {
307 return -ENODATA;
308 }
309 return 0;
310}
311
312int LE_command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
313 void *pCmdData, uint32_t *replySize, void *pReplyData) {
314
315 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *)self;
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700316
317 if (pContext == NULL || pContext->mState == LOUDNESS_ENHANCER_STATE_UNINITIALIZED) {
318 return -EINVAL;
319 }
320
321// ALOGV("LE_command command %d cmdSize %d",cmdCode, cmdSize);
322 switch (cmdCode) {
323 case EFFECT_CMD_INIT:
324 if (pReplyData == NULL || *replySize != sizeof(int)) {
325 return -EINVAL;
326 }
327 *(int *) pReplyData = LE_init(pContext);
328 break;
329 case EFFECT_CMD_SET_CONFIG:
330 if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
Eric Laurent0f714a42015-06-19 15:33:57 -0700331 || pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700332 return -EINVAL;
333 }
334 *(int *) pReplyData = LE_setConfig(pContext,
335 (effect_config_t *) pCmdData);
336 break;
337 case EFFECT_CMD_GET_CONFIG:
338 if (pReplyData == NULL ||
339 *replySize != sizeof(effect_config_t)) {
340 return -EINVAL;
341 }
342 LE_getConfig(pContext, (effect_config_t *)pReplyData);
343 break;
344 case EFFECT_CMD_RESET:
345 LE_reset(pContext);
346 break;
347 case EFFECT_CMD_ENABLE:
Eric Laurent0f714a42015-06-19 15:33:57 -0700348 if (pReplyData == NULL || replySize == NULL || *replySize != sizeof(int)) {
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700349 return -EINVAL;
350 }
351 if (pContext->mState != LOUDNESS_ENHANCER_STATE_INITIALIZED) {
352 return -ENOSYS;
353 }
354 pContext->mState = LOUDNESS_ENHANCER_STATE_ACTIVE;
355 ALOGV("EFFECT_CMD_ENABLE() OK");
356 *(int *)pReplyData = 0;
357 break;
358 case EFFECT_CMD_DISABLE:
359 if (pReplyData == NULL || *replySize != sizeof(int)) {
360 return -EINVAL;
361 }
362 if (pContext->mState != LOUDNESS_ENHANCER_STATE_ACTIVE) {
363 return -ENOSYS;
364 }
365 pContext->mState = LOUDNESS_ENHANCER_STATE_INITIALIZED;
366 ALOGV("EFFECT_CMD_DISABLE() OK");
367 *(int *)pReplyData = 0;
368 break;
369 case EFFECT_CMD_GET_PARAM: {
370 if (pCmdData == NULL ||
371 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700372 pReplyData == NULL || replySize == NULL ||
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700373 *replySize < (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t))) {
374 return -EINVAL;
375 }
376 memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(uint32_t));
377 effect_param_t *p = (effect_param_t *)pReplyData;
378 p->status = 0;
379 *replySize = sizeof(effect_param_t) + sizeof(uint32_t);
380 if (p->psize != sizeof(uint32_t)) {
381 p->status = -EINVAL;
382 break;
383 }
384 switch (*(uint32_t *)p->data) {
385 case LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB:
386 ALOGV("get target gain(mB) = %d", pContext->mTargetGainmB);
387 *((int32_t *)p->data + 1) = pContext->mTargetGainmB;
388 p->vsize = sizeof(int32_t);
389 *replySize += sizeof(int32_t);
390 break;
391 default:
392 p->status = -EINVAL;
393 }
394 } break;
395 case EFFECT_CMD_SET_PARAM: {
396 if (pCmdData == NULL ||
397 cmdSize != (int)(sizeof(effect_param_t) + sizeof(uint32_t) + sizeof(uint32_t)) ||
Eric Laurent0f714a42015-06-19 15:33:57 -0700398 pReplyData == NULL || replySize == NULL || *replySize != sizeof(int32_t)) {
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700399 return -EINVAL;
400 }
401 *(int32_t *)pReplyData = 0;
402 effect_param_t *p = (effect_param_t *)pCmdData;
403 if (p->psize != sizeof(uint32_t) || p->vsize != sizeof(uint32_t)) {
404 *(int32_t *)pReplyData = -EINVAL;
405 break;
406 }
407 switch (*(uint32_t *)p->data) {
408 case LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB:
409 pContext->mTargetGainmB = *((int32_t *)p->data + 1);
410 ALOGV("set target gain(mB) = %d", pContext->mTargetGainmB);
411 LE_reset(pContext); // apply parameter update
412 break;
413 default:
414 *(int32_t *)pReplyData = -EINVAL;
415 }
416 } break;
417 case EFFECT_CMD_SET_DEVICE:
418 case EFFECT_CMD_SET_VOLUME:
419 case EFFECT_CMD_SET_AUDIO_MODE:
420 break;
421
422 default:
423 ALOGW("LE_command invalid command %d",cmdCode);
424 return -EINVAL;
425 }
426
427 return 0;
428}
429
430/* Effect Control Interface Implementation: get_descriptor */
431int LE_getDescriptor(effect_handle_t self,
432 effect_descriptor_t *pDescriptor)
433{
434 LoudnessEnhancerContext * pContext = (LoudnessEnhancerContext *) self;
435
436 if (pContext == NULL || pDescriptor == NULL) {
437 ALOGV("LE_getDescriptor() invalid param");
438 return -EINVAL;
439 }
440
441 *pDescriptor = gLEDescriptor;
442
443 return 0;
444} /* end LE_getDescriptor */
445
446// effect_handle_t interface implementation for DRC effect
447const struct effect_interface_s gLEInterface = {
448 LE_process,
449 LE_command,
450 LE_getDescriptor,
451 NULL,
452};
453
454// This is the only symbol that needs to be exported
455__attribute__ ((visibility ("default")))
456audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
synergy dev9803acb2013-12-17 17:48:51 -0800457 .tag = AUDIO_EFFECT_LIBRARY_TAG,
458 .version = EFFECT_LIBRARY_API_VERSION,
459 .name = "Loudness Enhancer Library",
460 .implementor = "The Android Open Source Project",
461 .create_effect = LELib_Create,
462 .release_effect = LELib_Release,
463 .get_descriptor = LELib_GetDescriptor,
Jean-Michel Trivi6cc3a992013-09-10 09:15:18 -0700464};
465
466}; // extern "C"
467